Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
00f65f9
feat: split wallet app entries
hanszhoou Apr 20, 2022
4957651
chore: type
hanszhoou Apr 20, 2022
9653259
chore: type
hanszhoou Apr 20, 2022
a724a45
Merge branch 'develop' into hancheng/split-application-wallet-entry
hanszhoou Apr 22, 2022
993a4e1
feat: user guide
yanzhihong23 Apr 22, 2022
a6a0438
chore: solve conflict
hanszhoou Apr 26, 2022
3f84e7e
Revert "chore: solve conflict"
hanszhoou Apr 26, 2022
68358ed
Merge branch 'develop' into hancheng/split-application-wallet-entry
hanszhoou Apr 27, 2022
d6f5ed4
chore: solve conflict
hanszhoou Apr 27, 2022
5bf451e
feat: should open twitter when user click agree button on welcome page
Lanttcat Apr 12, 2022
912ea48
feat: be opened social network should determined by the caller
Lanttcat Apr 12, 2022
0366282
feat: merge develop
Lanttcat Apr 27, 2022
c66d357
feat: recommend feature app
hanszhoou Apr 27, 2022
dd64f52
chore: update event
hanszhoou Apr 27, 2022
1dfe7e9
Merge branch 'develop' into hancheng/split-application-wallet-entry
hanszhoou Apr 27, 2022
dc84ea1
chore: persona connect
hanszhoou Apr 27, 2022
086d846
fix: little fix
hanszhoou Apr 27, 2022
b2d96dd
Merge branch 'develop' into hancheng/split-application-wallet-entry
hanszhoou Apr 28, 2022
df51ff4
fix: dialog title text center
hanszhoou Apr 28, 2022
6dd82ca
fix: style
hanszhoou Apr 28, 2022
7fd0bdd
chore: add comment
hanszhoou Apr 28, 2022
caea3be
fix: ui style
hanszhoou Apr 28, 2022
321f852
chore: ui style
hanszhoou Apr 28, 2022
e69ad12
Merge branch 'hancheng/split-application-wallet-entry' into feat-user…
yanzhihong23 Apr 29, 2022
1d7751d
Merge branch 'feat-user-guide' into feat/setup-improve-for-testing
Lanttcat Apr 29, 2022
70533a9
Merge branch 'feat/dashboard-setup-optimize' into feat/setup-improve-…
Lanttcat Apr 29, 2022
e79aeb7
fix: can't open composition dialog when there is no persona
yanzhihong23 Apr 29, 2022
f463f61
fix: user guide step 3 not showing
yanzhihong23 May 6, 2022
bc7196e
fix: zh translation
yanzhihong23 May 6, 2022
6c1548e
Merge branch 'develop' into feat/setup-improve-for-testing
hanszhoou May 7, 2022
3f065ef
fix: identity collecting
yanzhihong23 May 9, 2022
019c658
fix: typo
yanzhihong23 May 9, 2022
33d87fe
Merge branch 'develop' into feat/setup-improve-for-testing
hanszhoou May 11, 2022
b9e02ad
feat: setup social network in current window
yanzhihong23 May 13, 2022
1da164b
chore: remove useSetupSocialNetwork hook
yanzhihong23 May 13, 2022
4e70de3
chore: reply review
yanzhihong23 May 13, 2022
8f4c1aa
feat: disable open composition dialog after user guide
yanzhihong23 May 13, 2022
26bb154
Revert "feat: disable open composition dialog after user guide"
yanzhihong23 May 14, 2022
bd1a0a5
feat: code review feedback
Lanttcat May 14, 2022
6b32288
refactor: remove unnecessary array deconstruction
Lanttcat May 14, 2022
e4a2fdd
refactor: use location assign to replace direction assignment
Lanttcat May 14, 2022
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
9 changes: 5 additions & 4 deletions packages/dashboard/src/pages/Welcome/index.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import { Button, useTheme } from '@mui/material'
import { useNavigate } from 'react-router-dom'
import { DashboardRoutes } from '@masknet/shared-base'
import { ColumnLayout } from '../../components/RegisterFrame/ColumnLayout'
import { styled } from '@mui/material/styles'
import { memo, MutableRefObject, useEffect, useMemo, useRef } from 'react'
import { useDashboardI18N } from '../../locales'
import links from '../../components/FooterLine/links.json'
import { openWindow } from '@masknet/shared-base-ui'
import { Services } from '../../API'

const Content = styled('div')(({ theme }) => ({
padding: `${theme.spacing(1)} ${theme.spacing(4)}`,
Expand All @@ -33,7 +32,6 @@ const IFrame = styled('iframe')(({ theme }) => ({
export default function Welcome() {
const iframeRef = useRef<HTMLIFrameElement | null>(null)
const mode = useTheme().palette.mode
const navigate = useNavigate()

const agreementContentPageURL = new URL('./en.html', import.meta.url).toString()
const privacyPolicyDocument = useMemo(() => () => iframeRef?.current?.contentWindow?.document, [iframeRef])
Expand Down Expand Up @@ -79,7 +77,10 @@ export default function Welcome() {
iframeRef={iframeRef}
privacyPolicyURL={agreementContentPageURL}
iframeLoadHandler={handleIFrameLoad}
agreeHandler={() => navigate(DashboardRoutes.Setup)}
agreeHandler={async () => {
const url = await Services.SocialNetwork.setupSocialNetwork('twitter.com', false)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Why do you set up Twitter by default?

if (url) location.assign(url)
}}
cancelHandler={() => window.close()}
/>
)
Expand Down
7 changes: 4 additions & 3 deletions packages/mask/shared-ui/locales/en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -111,9 +111,10 @@
"setup_guide_pin_tip_step_find_left": "Find Mask Network in the list of extensions and click the ",
"setup_guide_pin_tip_step_find_right": " button.",
"setup_guide_pin_tip_successfully": "Pinned successfully.",
"user_guide_tip_1": "Connect wallet to explore multi-chain dApps.",
"user_guide_tip_2": "Setup your exclusive NFT Avatar, explore the endless possibilities of Web 3.0.",
"user_guide_tip_3": "Click here to have a quick start.",
"user_guide_tip_1": "Explore multi-chain dApps.",
"user_guide_tip_2": "Connect and switch your wallet.",
"user_guide_tip_3": "Browse the Web 3 footprints of your Twitter friends, enjoy the freedom of Web 3.",
"user_guide_tip_4": "Click here to have a quick start.",
"create_persona": "Create persona",
"connect_persona": "Connect persona",
"please_create_persona": "Please create persona",
Expand Down
5 changes: 3 additions & 2 deletions packages/mask/shared-ui/locales/zh-CN.json
Original file line number Diff line number Diff line change
Expand Up @@ -106,8 +106,9 @@
"setup_guide_pin_tip_step_find_right": " 按钮。",
"setup_guide_pin_tip_successfully": "固定成功。",
"user_guide_tip_1": "点击这里快速访问主要功能。",
"user_guide_tip_2": "点击这里连接您的钱包。您可以在此选择网络或更改您的钱包。",
"user_guide_tip_3": "点击这里快速开始。",
"user_guide_tip_2": "点击这里连接或更改您的钱包。",
"user_guide_tip_3": "浏览 Twitter 好友的 Web 3 足迹,畅享 Web 3 的自由。",
"user_guide_tip_4": "尝试快速开始探索 Web 3 世界。",
"create_persona": "创建身份",
"connect_persona": "连接身份",
"please_create_persona": "请创建身份",
Expand Down
6 changes: 5 additions & 1 deletion packages/mask/src/components/GuideStep/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,10 @@ export default function GuideStep({
if (disabled) return
const open = +lastStep === step
setOpen(open)

if (!open) return
if (location.pathname === '/home') return
location.assign('/home')
}, [lastStep])

useEffect(() => {
Expand Down Expand Up @@ -169,7 +173,7 @@ export default function GuideStep({
return () => {
window.removeEventListener('resize', onResize)
}
}, [childrenRef])
}, [childrenRef, lastStep, open])

return (
<>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ const EntryIconButton = memo((props: PostDialogHintUIProps) => {
return disableGuideTip ? (
getEntry()
) : (
<GuideStep step={3} total={3} tip={t('user_guide_tip_3')} onComplete={props.onHintButtonClicked}>
<GuideStep step={4} total={4} tip={t('user_guide_tip_4')} onComplete={props.onHintButtonClicked}>
{getEntry()}
</GuideStep>
)
Expand Down
55 changes: 26 additions & 29 deletions packages/mask/src/components/InjectedComponents/ToolboxUnstyled.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import { AccountBalanceWalletIcon } from '@masknet/icons'
import { makeStyles } from '@masknet/theme'
import FiberManualRecordIcon from '@mui/icons-material/FiberManualRecord'
import { NextIDVerificationStatus, useNextIDConnectStatus } from '../DataSource/useNextID'
import { MaskIcon } from '../../resources/MaskIcon'

const useStyles = makeStyles()((theme) => ({
title: {
Expand Down Expand Up @@ -100,31 +101,27 @@ function ToolboxHintForApplication(props: ToolboxHintProps) {
const { t } = useI18N()
const { openDialog } = useRemoteControlledDialog(WalletMessages.events.ApplicationDialogUpdated)
return (
<Container>
<ListItemButton onClick={openDialog}>
<img
src={new URL('../../plugins/EVM/assets/maskwallet.png', import.meta.url).toString()}
style={{
width: iconSize,
height: iconSize,
}}
/>
{mini ? null : (
<ListItemText
primary={
<Box
sx={{
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
}}>
<Typography className={classes.title}>{t('mask_network')}</Typography>
</Box>
}
/>
)}
</ListItemButton>
</Container>
<GuideStep step={1} total={4} tip={t('user_guide_tip_1')}>
<Container>
<ListItemButton onClick={openDialog}>
<MaskIcon style={{ width: iconSize, height: iconSize }} />
{mini ? null : (
<ListItemText
primary={
<Box
sx={{
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
}}>
<Typography className={classes.title}>{t('mask_network')}</Typography>
</Box>
}
/>
)}
</ListItemButton>
</Container>
</GuideStep>
)
}

Expand Down Expand Up @@ -157,8 +154,8 @@ function ToolboxHintForWallet(props: ToolboxHintProps) {

return (
<>
<GuideStep step={1} total={3} tip={t('user_guide_tip_1')}>
<Container>
<Container>
<GuideStep step={2} total={4} tip={t('user_guide_tip_2')}>
<ListItemButton onClick={openWallet}>
<ListItemIcon>
{isWalletValid ? (
Expand Down Expand Up @@ -196,8 +193,8 @@ function ToolboxHintForWallet(props: ToolboxHintProps) {
/>
)}
</ListItemButton>
</Container>
</GuideStep>
</GuideStep>
</Container>
</>
)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
import { definedSocialNetworkUIs, getNetworkWorker, loadSocialNetworkUI, SocialNetworkUI } from '../../social-network'
import { Flags } from '../../../shared'

import { currentSetupGuideStatus } from '../../settings/settings'
import { currentSetupGuideStatus, userGuideStatus } from '../../settings/settings'
import stringify from 'json-stable-stringify'
import { SetupGuideStep } from '../../components/InjectedComponents/SetupGuide/types'
import type { PersonaIdentifier, ProfileIdentifier } from '@masknet/shared-base'
import { delay } from '@dimensiondev/kit'
import { requestExtensionPermission } from '../../../background/services/helper'
import {
definedSocialNetworkUIs,
getNetworkWorker,
loadSocialNetworkUI,
loadSocialNetworkUIs,
SocialNetworkUI,
} from '../../social-network'
import { Flags } from '../../../shared'

export async function getDefinedSocialNetworkUIs() {
return [...definedSocialNetworkUIs.values()].map(({ networkIdentifier }) => {
Expand All @@ -22,6 +27,30 @@ function requestSNSAdaptorPermission(ui: SocialNetworkUI.Definition) {
return requestExtensionPermission({ origins: [...ui.declarativePermissions.origins] })
}

function requestSNSAdaptorsPermission(uis: SocialNetworkUI.Definition[]) {
Comment thread
Lanttcat marked this conversation as resolved.
return requestExtensionPermission({
origins: uis.map((x) => x.declarativePermissions.origins).flat(),
})
}

export async function setupSocialNetwork(defaultNetwork: string, newTab = true) {
const ui = await loadSocialNetworkUI(defaultNetwork)
const home = ui.utils.getHomePage?.()

const uis = await loadSocialNetworkUIs()
if (!Flags.no_web_extension_dynamic_permission_request) {
if (!(await requestSNSAdaptorsPermission(uis))) return
}

userGuideStatus[defaultNetwork].value = '1'
await delay(100)
if (!home) return
if (!newTab) return home

browser.tabs.create({ active: true, url: home })
return
}

export async function connectSocialNetwork(
identifier: PersonaIdentifier,
network: string,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,49 @@ import {
searchSelfHandleSelector,
searchSelfNicknameSelector,
searchSelfAvatarSelector,
selfInfoSelectors,
} from '../utils/selector'
import { creator, SocialNetworkUI as Next } from '../../../social-network'
import Services from '../../../extension/service'
import { twitterBase } from '../base'
import { getAvatar, getBio, getNickname, getTwitterId, getPersonalHomepage } from '../utils/user'
import { isMobileTwitter } from '../utils/isMobile'

function recognizeDesktop() {
const collect = () => {
const handle = selfInfoSelectors().handle.evaluate()
const nickname = selfInfoSelectors().name.evaluate()
const avatar = selfInfoSelectors().userAvatar.evaluate()

return { handle, nickname, avatar }
}

const watcher = new MutationObserverWatcher(selfInfoSelectors().handle)

return { watcher, collect }
}

function recognizeMobile() {
const collect = () => {
const avatar = searchSelfAvatarSelector().evaluate()?.getAttribute('src') ?? ''
const handle = searchSelfHandleSelector().evaluate()?.textContent?.trim()?.replace(/^@/, '')
const nickname = searchSelfNicknameSelector().evaluate()?.textContent?.trim() ?? ''

return { handle, nickname, avatar }
}

const watcher = new MutationObserverWatcher(searchSelfHandleSelector())

return { watcher, collect }
}

function resolveLastRecognizedIdentityInner(
ref: Next.CollectingCapabilities.IdentityResolveProvider['recognized'],
cancel: AbortSignal,
) {
const task = isMobileTwitter ? recognizeMobile() : recognizeDesktop()
const assign = () => {
const avatar = searchSelfAvatarSelector().evaluate()?.getAttribute('src') ?? ''
const handle = searchSelfHandleSelector().evaluate()?.textContent?.trim()?.replace(/^@/, '')
const nickname = searchSelfNicknameSelector().evaluate()?.textContent?.trim() ?? ''
const { handle, nickname, avatar } = task.collect()

if (handle) {
ref.value = {
Expand All @@ -32,7 +60,7 @@ function resolveLastRecognizedIdentityInner(
}
}
}
const watcher = new MutationObserverWatcher(searchSelfHandleSelector())
const watcher = task.watcher
.addListener('onAdd', () => assign())
.addListener('onChange', () => assign())
.startWatch({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,5 @@ export function injectToolboxHintAtTwitter(signal: AbortSignal, category: 'walle
function injectProfile(signal: AbortSignal) {
const watcher = new MutationObserverWatcher(sideBarProfileSelector())
startWatch(watcher, signal)
createReactRootShadowed(watcher.firstDOMProxy.afterShadow, { signal }).render(<ProfileLinkAtTwitter />)
createReactRootShadowed(watcher.firstDOMProxy.beforeShadow, { signal }).render(<ProfileLinkAtTwitter />)
}
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ export function ToolboxHintAtTwitter(props: { category: 'wallet' | 'application'
export function ProfileLinkAtTwitter() {
const { t } = useI18N()
return (
<GuideStep step={2} total={3} tip={t('user_guide_tip_2')}>
<GuideStep step={3} total={4} tip={t('user_guide_tip_3')}>
<Box sx={{ position: 'absolute', left: 0, right: 0, width: '100%', height: '100%' }} />
</GuideStep>
)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { LiveSelector } from '@dimensiondev/holoflows-kit'
import { regexMatch } from '../../../utils/utils'
import { isMobileTwitter } from './isMobile'
import { isCompose } from './postBox'

Expand Down Expand Up @@ -113,7 +114,7 @@ export const postEditorInPopupSelector: () => LiveSelector<E, true> = () =>
export const toolBoxInSideBarSelector: () => LiveSelector<E, true> = () =>
querySelector<E>('[role="banner"] [role="navigation"] > div')
export const sideBarProfileSelector: () => LiveSelector<E, true> = () =>
querySelector<E>('[role="banner"] [role="navigation"] [aria-label="Lists"] > div')
querySelector<E>('[role="banner"] [role="navigation"] [aria-label="Profile"] > div')
export const postEditorInTimelineSelector: () => LiveSelector<E, true> = () =>
querySelector<E>('[role="main"] :not(aside) > [role="progressbar"] ~ div [role="button"][aria-label]:nth-child(6)')

Expand Down Expand Up @@ -216,6 +217,25 @@ export const postsContentSelector = () =>
export const postAvatarsContentSelector = () =>
querySelectorAll('[data-testid="tweet"] > div > div > div > :nth-child(2)')

const base = querySelector<HTMLScriptElement>('#react-root + script')
const handle = /"screen_name":"(.*?)"/
const name = /"name":"(.*?)"/
const bio = /"description":"(.*?)"/
const avatar = /"profile_image_url_https":"(.*?)"/
/**
* first matched element can be extracted by index zero, followings are all capture groups, if no 'g' specified.
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/match
*/
const p = (regex: RegExp, index: number) => {
return base.clone().map((x) => regexMatch(x.innerText, regex, index))
}
export const selfInfoSelectors = () => ({
handle: p(handle, 1),
name: p(name, 1),
bio: p(bio, 1),
userAvatar: p(avatar, 1),
})

// #region self info
export const searchSelfHandleSelector = () => {
return querySelector<HTMLSpanElement>(
Expand Down
14 changes: 13 additions & 1 deletion packages/mask/src/social-network/ui.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,6 @@ export async function activateSocialNetworkUIInner(ui_deferred: SocialNetworkUI.
ui.injection.toolbox?.(signal, 'wallet')
ui.injection.toolbox?.(signal, 'application')
}
ui.injection.setupPrompt?.(signal)
ui.injection.newPostComposition?.start?.(signal)
ui.injection.searchResult?.(signal)
ui.injection.userBadge?.(signal)
Expand Down Expand Up @@ -229,6 +228,19 @@ export async function activateSocialNetworkUIInner(ui_deferred: SocialNetworkUI.
}
}

export async function loadSocialNetworkUIs(): Promise<SocialNetworkUI.Definition[]> {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Why you're going to load every SNS Adaptors?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

We need to have the user authorize all SNS before creating persona.

const defines = [...definedSocialNetworkUIs.values()].map(async (x) => x.load())
const uis = (await Promise.all(defines)).map((x) => x.default)

if (!defines) throw new Error('SNS adaptor load failed')

for (const ui of uis) {
definedSocialNetworkUIsResolved.set(ui.networkIdentifier, ui)
}

return uis
}

export async function loadSocialNetworkUI(identifier: string): Promise<SocialNetworkUI.Definition> {
if (definedSocialNetworkUIsResolved.has(identifier)) return definedSocialNetworkUIsResolved.get(identifier)!
const define = definedSocialNetworkUIs.get(identifier)
Expand Down
Loading