Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .env/dev-preference.example
Original file line number Diff line number Diff line change
Expand Up @@ -104,3 +104,4 @@

; FIREFLY_X_CLIENT_ID=
; FIREFLY_X_CLIENT_SECRET=
; PRIVY_APP_ID=
1 change: 1 addition & 0 deletions .pnpmfile.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ approvedList.set('string-width-cjs', 'npm:string-width@^4.2.0')
approvedList.set('strip-ansi-cjs', 'npm:strip-ansi@^6.0.1')
approvedList.set('wrap-ansi-cjs', ['npm:wrap-ansi@^6.0.1', 'npm:wrap-ansi@^7.0.0'])

approvedList.set('cbw-sdk', ['npm:@coinbase/[email protected]'])
/**
* @param {string} parentPackage The current resolving parentPackage
* @param {string} dependedPackage The package it depends on
Expand Down
5 changes: 4 additions & 1 deletion cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@
"packages/xcode",
"pnpm-lock.yaml",
"pnpm-workspace.yaml",
"qya-aa.json"
"qya-aa.json",
".env/dev-preference.example"
],
"TODO: fix those words": ["bridgable", "clonable", "sniffings"],
"ignoreWords": [
Expand Down Expand Up @@ -246,6 +247,7 @@
"words": [
"arbitrum",
"boba",
"caip",
"cashtags",
"celo",
"endregion",
Expand All @@ -259,6 +261,7 @@
"tiktok",
"tweetnacl",
"txid",
"wagmi",
"waitlist",
"youtube"
]
Expand Down
7 changes: 5 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@
"spellcheck": "cspell lint --no-must-find-files",
"clean": "gulp clean",
"lingui:compile": "gulp lingui-compile",
"lingui:extract": "gulp lingui-extract"
"lingui:extract": "gulp lingui-extract",
"lingui": "pnpm run lingui:extract && pnpm run lingui:compile"
},
"dependencies": {
"@dimensiondev/holoflows-kit": "0.9.0-20240322092738-f9180f3",
Expand All @@ -45,6 +46,7 @@
"@mui/lab": "5.0.0-alpha.170",
"@mui/material": "5.15.20",
"@mui/system": "5.15.20",
"@privy-io/react-auth": "^3.0.1",
"@tanstack/react-query": "^5.49.2",
"@types/masknet__global-types": "workspace:^",
"@types/react": "npm:types-react@beta",
Expand All @@ -55,7 +57,8 @@
"react": "0.0.0-experimental-58af67a8f8-20240628",
"react-dom": "0.0.0-experimental-58af67a8f8-20240628",
"ses": "1.12.0",
"ts-results-es": "^4.2.0"
"ts-results-es": "^4.2.0",
"wagmi": "^2.17.1"
},
"devDependencies": {
"@changesets/cli": "^2.28.1",
Expand Down
1 change: 1 addition & 0 deletions packages/mask/.webpack/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,7 @@ export async function createConfiguration(
LOAD_KEY: process.env.LOAD_KEY || '',
FIREFLY_X_CLIENT_ID: process.env.FIREFLY_X_CLIENT_ID || '',
FIREFLY_X_CLIENT_SECRET: process.env.FIREFLY_X_CLIENT_SECRET || '',
PRIVY_APP_ID: process.env.PRIVY_APP_ID || '',
}),
new (rspack?.DefinePlugin || webpack.default.DefinePlugin)({
'process.browser': 'true',
Expand Down
7 changes: 0 additions & 7 deletions packages/mask/.webpack/flags.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,6 @@ export interface BuildFlags {
sourceMapPreference?: boolean | string
/** @default true */
sourceMapHideFrameworks?: boolean | undefined
FIREFLY_X_CLIENT_ID?: string
FIREFLY_X_CLIENT_SECRET?: string
}
export type NormalizedFlags = Required<BuildFlags>
export function normalizeBuildFlags(flags: BuildFlags): NormalizedFlags {
Expand All @@ -52,8 +50,6 @@ export function normalizeBuildFlags(flags: BuildFlags): NormalizedFlags {
reactCompiler = false,
lavamoat = false,
csp = false,
FIREFLY_X_CLIENT_ID = '',
FIREFLY_X_CLIENT_SECRET = '',
} = flags
let {
hmr = mode === 'development',
Expand Down Expand Up @@ -91,9 +87,6 @@ export function normalizeBuildFlags(flags: BuildFlags): NormalizedFlags {
devtoolsEditorURI,
// CI / profiling
profiling,
// Secrets
FIREFLY_X_CLIENT_ID,
FIREFLY_X_CLIENT_SECRET,
}
}

Expand Down
2 changes: 1 addition & 1 deletion packages/mask/.webpack/rspack.config.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { createConfiguration } from './config.js'
import { createConfiguration } from './config.ts'
export default async function (cli_env) {
const flags = JSON.parse(Buffer.from(cli_env.flags, 'hex').toString('utf-8'))
return createConfiguration(true, flags)
Expand Down
34 changes: 34 additions & 0 deletions packages/mask/background/services/helper/firefly.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { PersistentStorages } from '@masknet/shared-base'

export async function loginFireflyViaTwitter() {
const data = await browser.storage.local.get('firefly_x_oauth')
if (!data?.firefly_x_oauth) throw new Error('X OAuth token not found')
const oauth: Record<string, string> = data.firefly_x_oauth

const res = await fetch('https://firefly.social/api/twitter/auth', {
method: 'POST',
headers: {
'X-Access-Token': oauth.oauth_token,
'X-Access-Token-Secret': oauth.oauth_token_secret,
'X-Client-Id': oauth.oauth_token.split('-')[0],
'X-Consumer-Key': process.env.FIREFLY_X_CLIENT_ID,
'X-Consumer-Secret': '[HIDE_FROM_CLIENT]',
Copy link

Copilot AI Oct 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hardcoded placeholder '[HIDE_FROM_CLIENT]' for consumer secret suggests incomplete implementation. This should use the actual environment variable or be properly handled to avoid authentication failures.

Suggested change
'X-Consumer-Secret': '[HIDE_FROM_CLIENT]',
'X-Consumer-Secret': process.env.FIREFLY_X_CONSUMER_SECRET,

Copilot uses AI. Check for mistakes.

},
})
const json = await res.json()
if (!json.success) throw new Error(json.message)

const res2 = await fetch('https://api.firefly.land/v3/auth/exchange/twitter', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
data: json.data,
}),
})
const json2 = await res2.json()
await browser.storage.local.set({ firefly_account: json2.data })
await PersistentStorages.Settings.storage.firefly_account.setValue(json2.data)
return json2.data
}
1 change: 1 addition & 0 deletions packages/mask/background/services/helper/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,4 @@ export { getTelemetryID, setTelemetryID } from './telemetry-id.js'
export { fetchSandboxedPluginManifest } from './sandboxed.js'
export { getActiveTab } from './tabs.js'
export { requestXOAuthToken, resolveXOAuth, resetXOAuth } from './oauth-x.js'
export { loginFireflyViaTwitter } from './firefly.js'
2 changes: 1 addition & 1 deletion packages/mask/background/services/helper/oauth-x.ts
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ export async function requestXOAuthToken() {
const user_id = step2.get('user_id')
const screen_name = step2.get('screen_name')

browser.storage.local.set({
await browser.storage.local.set({
firefly_x_oauth: {
oauth_token,
oauth_token_secret,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { useEffect } from 'react'
import { useCustomSnackbar } from '@masknet/theme'
import { Button, Box, Typography } from '@mui/material'
import { Trans } from '@lingui/react/macro'
import { createInjectHooksRenderer, useActivatedPluginsSiteAdaptor } from '@masknet/plugin-infra/content-script'
import { MaskMessages } from '@masknet/shared-base'
import { useMatchXS } from '@masknet/shared-base-ui'
import { useCustomSnackbar } from '@masknet/theme'
import { Box, Button, Typography } from '@mui/material'
import { useEffect } from 'react'
import { useAutoPasteFailedDialog } from './AutoPasteFailedDialog.js'
import { Trans } from '@lingui/react/macro'

const GlobalInjection = createInjectHooksRenderer(
useActivatedPluginsSiteAdaptor.visibility.useAnyMode,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { useCallback, useLayoutEffect, useState } from 'react'
import { MutationObserverWatcher } from '@dimensiondev/holoflows-kit'
import { makeStyles } from '@masknet/theme'
import { Trans } from '@lingui/react/macro'
import { MaskMessages } from '@masknet/shared-base'
import { useLocationChange } from '@masknet/shared-base-ui'
import { makeStyles } from '@masknet/theme'
import { useCallback, useLayoutEffect, useState } from 'react'
import { attachReactTreeWithContainer } from '../../../../utils/shadow-root.js'
import { startWatch } from '../../../../utils/startWatch.js'
import { searchInstagramAvatarEditPageSettingDialog, searchInstagramAvatarListSelector } from '../../utils/selector.js'
import { attachReactTreeWithContainer } from '../../../../utils/shadow-root.js'
import { NFTAvatarSettingDialog } from './NFTAvatarSettingDialog.js'
import { Trans } from '@lingui/react/macro'

export async function injectProfileNFTAvatarInInstagram(signal: AbortSignal) {
const watcher = new MutationObserverWatcher(searchInstagramAvatarListSelector())
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { MutationObserverWatcher } from '@dimensiondev/holoflows-kit'
import { PrivySetup } from '@masknet/shared'
import { ValueRef } from '@masknet/shared-base'
import { useValueRef } from '@masknet/shared-base-ui'
import { RootWeb3ContextProvider } from '@masknet/web3-hooks-base'
import { MutationObserverWatcher } from '@dimensiondev/holoflows-kit'
import { startWatch } from '../../../utils/startWatch.js'
import { attachReactTreeWithContainer } from '../../../utils/shadow-root/renderInShadowRoot.js'
import { startWatch } from '../../../utils/startWatch.js'
import { querySelector, sideBarProfileSelector } from '../utils/selector.js'
import { ProfileLinkAtTwitter, ToolboxHintAtTwitter } from './ToolboxHint_UI.js'

Expand All @@ -28,6 +29,7 @@ export function injectToolboxHintAtTwitter(signal: AbortSignal, category: 'walle
})
attachReactTreeWithContainer(watcher.firstDOMProxy.afterShadow, { signal }).render(
<RootWeb3ContextProvider>
<PrivySetup />
<ToolboxHintAtTwitter category={category} />
</RootWeb3ContextProvider>,
)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,37 +1,51 @@
import { cloneElement, Suspense } from 'react'
import { i18n } from '@lingui/core'
import { useSiteThemeMode } from '@masknet/plugin-infra/content-script'
import { LinguiProviderHMR, SharedContextProvider, PrivySetup } from '@masknet/shared'
import { queryClient } from '@masknet/shared-base-ui'
import { DialogStackingProvider, MaskThemeProvider } from '@masknet/theme'
import { RootWeb3ContextProvider } from '@masknet/web3-hooks-base'
import { QueryClientProvider } from '@tanstack/react-query'
import { ReactQueryDevtools } from '@tanstack/react-query-devtools'
import { RootWeb3ContextProvider } from '@masknet/web3-hooks-base'
import { DialogStackingProvider, MaskThemeProvider } from '@masknet/theme'
import { jsxCompose } from '@masknet/shared-base'
import { queryClient } from '@masknet/shared-base-ui'
import { createPortal } from 'react-dom'
import { i18n } from '@lingui/core'
import { PersistQueryClientProvider } from '@tanstack/react-query-persist-client'
import { Suspense } from 'react'
import { createPortal } from 'react-dom'
import { queryPersistOptions } from '../../../shared-ui/utils/persistOptions.js'
import { LinguiProviderHMR, SharedContextProvider } from '@masknet/shared'
import { useSiteThemeMode } from '@masknet/plugin-infra/content-script'
import { useMaskSiteAdaptorMixedTheme } from '../../components/useMaskSiteAdaptorMixedTheme.js'

export function ContentScriptGlobalProvider(children: React.ReactNode) {
return jsxCompose(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why remove the usage of jsxCompose?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is suitable to compose single-child jsx, if there are multiple children, it might end up xml style. You can see there is a <PrivySetup/> alongside <SharedContextProvider /> as RootWeb3ContextProvider's children

Besides, it causes every component renders twice, once for the original, once for the cloned one.

Copy link
Member

@Jack-Works Jack-Works Oct 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you mean by renders twice? Does that mean the JSX tree is being duplicated in the final rendering tree?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you mean by renders twice? Does that mean the JSX tree is being duplicated in the final rendering tree?

Sorry, it might be that I misread it at the time.

<Suspense />,
<DialogStackingProvider hasGlobalBackdrop={false} />,
<QueryClientProvider client={queryClient} />,
<PersistQueryClientProvider client={queryClient} persistOptions={queryPersistOptions} />,
<RootWeb3ContextProvider />,
<SharedContextProvider />,
<LinguiProviderHMR i18n={i18n} />,
// eslint-disable-next-line react-compiler/react-compiler
<MaskThemeProvider useMaskIconPalette={useSiteThemeMode} useTheme={useMaskSiteAdaptorMixedTheme} />,
)(
cloneElement,
const jsx =
process.env.NODE_ENV === 'development' ?
<>
{/* https://github.com/TanStack/query/issues/5417 */}
{createPortal(<ReactQueryDevtools buttonPosition="bottom-right" />, document.body)}
{children}
</>
Comment on lines 18 to 22
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Multiple children should go here. Does that answer your question?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think xml style would be more convenient, it's inconvenient to add siblings. Like if you prepend a sibling before <DialogStackingProvider />, should we move all descendant nodes to the callback parameter, and keep descendant nodes in xml or compose them with another jsxCompose()?

I think xml is better to manage and present hierarchical relationship.

To avoid deep indent, we can reduce the indent width, that's why I ignored prettier for this, or we can group those providers by dimensions, like <IOProvider/> groups io-related providers, etc.

Anyway, I can revert to jsxCompose if you insist.

: children,
: children

// eslint-disable: react-compiler/react-compiler
// prettier-ignore
return (
<Suspense>
<DialogStackingProvider hasGlobalBackdrop={false}>
<QueryClientProvider client={queryClient}>
<PersistQueryClientProvider client={queryClient} persistOptions={queryPersistOptions}>
<RootWeb3ContextProvider>
<PrivySetup />
<SharedContextProvider>
<LinguiProviderHMR i18n={i18n}>
<MaskThemeProvider
// eslint-disable-next-line react-compiler/react-compiler
useMaskIconPalette={useSiteThemeMode}
// eslint-disable-next-line react-compiler/react-compiler
useTheme={useMaskSiteAdaptorMixedTheme}>
{jsx}
</MaskThemeProvider>
</LinguiProviderHMR>
</SharedContextProvider>
</RootWeb3ContextProvider>
</PersistQueryClientProvider>
</QueryClientProvider>
</DialogStackingProvider>
</Suspense>
)
}
Original file line number Diff line number Diff line change
@@ -1,24 +1,21 @@
import { cloneElement, Suspense } from 'react'
import { Suspense } from 'react'
import { CSSVariableInjector, CustomSnackbarProvider } from '@masknet/theme'
import { ErrorBoundary } from '@masknet/shared-base-ui'
import { Sniffings, jsxCompose } from '@masknet/shared-base'
import { Sniffings } from '@masknet/shared-base'

// Providers added here will be added to ALL ShadowRoots, if you're mean to add a global one, add it in ./SiteUIProvider.tsx
export function ShadowRootAttachPointRoot(children: React.ReactNode) {
return jsxCompose(
<Suspense />,
<ErrorBoundary />,
<CustomSnackbarProvider
disableWindowBlurListener={false}
anchorOrigin={{ vertical: 'top', horizontal: 'right' }}
children={null!}
offsetY={Sniffings.is_facebook_page ? 80 : undefined}
/>,
)(
cloneElement,
<>
<CSSVariableInjector />
{children}
</>,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let lines of code, and clear hierarchical relation at a glance.

return (
<Suspense>
<ErrorBoundary>
<CustomSnackbarProvider
disableWindowBlurListener={false}
anchorOrigin={{ vertical: 'top', horizontal: 'right' }}
offsetY={Sniffings.is_facebook_page ? 80 : undefined}>
<CSSVariableInjector />
{children}
</CustomSnackbarProvider>
</ErrorBoundary>
</Suspense>
)
}
Loading