Skip to content

Commit

Permalink
refactor: avatar
Browse files Browse the repository at this point in the history
refactor: remove unused components

chore: tidy up components

refactor: store helpers

chore: impl store

refactor: avatar components

refactor: cleanup types

refactor: remove mobile sniffing
  • Loading branch information
guanbinrui committed Apr 24, 2024
1 parent d11dc54 commit 11f7c46
Show file tree
Hide file tree
Showing 102 changed files with 1,127 additions and 1,581 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { useState } from 'react'
import { useCopyToClipboard } from 'react-use'
import { useMaskSharedTrans } from '../../../shared-ui/index.js'
import { format as formatDateTime } from 'date-fns'
import { makeStyles, useCustomSnackbar } from '@masknet/theme'
import {
Expand All @@ -22,10 +21,11 @@ import { useMatchXS } from '@masknet/shared-base-ui'
import { DraggableDiv } from '../shared/DraggableDiv.js'
import { Close as CloseIcon, Download, OpenInBrowser } from '@mui/icons-material'
import { saveFileFromUrl } from '../../../shared/index.js'
import { useMaskSharedTrans } from '../../../shared-ui/index.js'

interface AutoPasteFailedDialogProps {
onClose: () => void
data: AutoPasteFailedEvent
onClose: () => void
}
const useStyles = makeStyles()((theme) => ({
title: { marginLeft: theme.spacing(1) },
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { Icons } from '@masknet/icons'
import { MaskColors, ShadowRootTooltip, makeStyles } from '@masknet/theme'
import { IconButton, Typography } from '@mui/material'
import { IconButton } from '@mui/material'
import { memo } from 'react'
import { isMobileFacebook } from '../../site-adaptors/facebook.com/utils/isMobile.js'
import { useMaskSharedTrans } from '../../../shared-ui/index.js'
import GuideStep from '../GuideStep/index.js'

Expand All @@ -21,19 +20,7 @@ interface PostDialogHintUIProps extends withClasses<'buttonTransform' | 'iconBut

const useStyles = makeStyles()((theme) => ({
button: {
// TODO: is it correct? (what about twitter?)
padding: isMobileFacebook ? 0 : 'var(--icon-padding, 10px)',
},
text: {
color: theme.palette.grey[300],
marginLeft: theme.spacing(1),
},
wrapper: {
display: 'flex',
alignItems: 'center',
width: '100%',
padding: '8px 10px',
borderBottom: `1px solid ${theme.palette.divider}`,
padding: 'var(--icon-padding, 10px)',
},
}))

Expand Down Expand Up @@ -84,15 +71,6 @@ export const PostDialogHint = memo(function PostDialogHintUI(props: PostDialogHi
const { onHintButtonClicked, size, ...others } = props
const { classes } = useStyles(undefined, { props })
const t = useMaskSharedTrans()

if (isMobileFacebook)
return (
<div className={classes.wrapper} onClick={onHintButtonClicked}>
<EntryIconButton size={size} onHintButtonClicked={() => undefined} />
<Typography className={classes.text}>{t.post_modal_hint__button()}</Typography>
</div>
)

return (
<div className={classes.buttonTransform}>
<EntryIconButton size={size} onHintButtonClicked={onHintButtonClicked} {...others} />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
import { Twitter } from '@masknet/web3-providers'
import { RSS3_KEY_SITE, NFTBadgeTimeline } from '@masknet/plugin-avatar'
import { NFTBadgeTimeline } from '@masknet/plugin-avatar'
import { useQuery } from '@tanstack/react-query'

interface Props {
className?: string
clipPathId: string
size: number
userId?: string
}
export function AvatarDecoration({ clipPathId, userId, className, size }: Props) {
export function AvatarDecoration({ userId, className, size }: Props) {
const { data: user } = useQuery({
queryKey: ['twitter', 'profile', 'check-nft-avatar', userId],
queryFn: () => {
Expand All @@ -26,7 +25,6 @@ export function AvatarDecoration({ clipPathId, userId, className, size }: Props)
avatarId={Twitter.getAvatarId(user.avatarURL)}
height={size}
width={size}
siteKey={RSS3_KEY_SITE.TWITTER}
/>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -157,12 +157,7 @@ export const ProfileBar = memo<ProfileBarProps>(function ProfileBar({
},
}}
/>
<AvatarDecoration
className={classes.avatarDecoration}
clipPathId={avatarClipPathId}
userId={identity.identifier?.userId}
size={40}
/>
<AvatarDecoration className={classes.avatarDecoration} userId={identity.identifier?.userId} size={40} />
</div>
<Box className={classes.description}>
<Typography className={classes.nickname} title={identity.nickname}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import type { SiteAdaptorUI } from '@masknet/types'
import { inputText, pasteText } from '@masknet/injected-script'
import { delay, waitDocumentReadyState } from '@masknet/kit'
import { MaskMessages } from '@masknet/shared-base'
import { isMobileFacebook } from '../utils/isMobile.js'

/**
* Access: https://(www|m).facebook.com/
Expand All @@ -23,9 +22,8 @@ export async function pasteTextToCompositionFacebook(

const activated = new LiveSelector().querySelectorAll<HTMLDivElement | HTMLTextAreaElement>(
// cspell:disable-next-line
isMobileFacebook ? 'form textarea' : 'div[role=presentation] .notranslate[role=textbox]',
'div[role=presentation] .notranslate[role=textbox]',
)
if (isMobileFacebook) activated.filter((x) => x.getClientRects().length > 0)

// Select element with fb customize background image.
const activatedCustom = new LiveSelector().querySelectorAll<HTMLDivElement | HTMLTextAreaElement>(
Expand Down Expand Up @@ -53,10 +51,6 @@ export async function pasteTextToCompositionFacebook(
if ('value' in document.activeElement!) inputText(text)
else pasteText(text)
await delay(200)
if (isMobileFacebook) {
const e = document.querySelector<HTMLDivElement | HTMLTextAreaElement>('.mentions-placeholder')
if (e) e.style.display = 'none'
}
// Prevent Custom Paste failed, this will cause service not available to user.
if (!element.innerText.includes(text) || ('value' in element && !element.value.includes(text)))
copyFailed('Not detected')
Expand Down
Original file line number Diff line number Diff line change
@@ -1,33 +1,20 @@
import { selectElementContents } from '../../../utils/selectElementContents.js'
import { delay } from '@masknet/kit'
import { isMobileFacebook } from '../utils/isMobile.js'
import type { PostInfo } from '@masknet/plugin-infra/content-script'
import { inputText, pasteText } from '@masknet/injected-script'
import { pasteText } from '@masknet/injected-script'
import { MaskMessages } from '@masknet/shared-base'

export async function pasteToCommentBoxFacebook(encryptedComment: string, current: PostInfo, dom: HTMLElement | null) {
const fail = () => {
MaskMessages.events.autoPasteFailed.sendToLocal({ text: encryptedComment })
}
if (isMobileFacebook) {
const root = dom || current.comment?.commentBoxSelector?.evaluate()[0]
if (!root) return fail()
const textarea = root.querySelector('textarea')
if (!textarea) return fail()
textarea.focus()
inputText(encryptedComment)
textarea.dispatchEvent(new CustomEvent('input', { bubbles: true, cancelable: false, composed: true }))
await delay(200)
if (!root.innerText.includes(encryptedComment)) return fail()
} else {
const root = dom || current.rootNode
if (!root) return fail()
const input = root.querySelector<HTMLElement>('[contenteditable] > *')
if (!input) return fail()
selectElementContents(input)
input.focus()
pasteText(encryptedComment)
await delay(200)
if (!root.innerText.includes(encryptedComment)) return fail()
}
const root = dom || current.rootNode
if (!root) return fail()
const input = root.querySelector<HTMLElement>('[contenteditable] > *')
if (!input) return fail()
selectElementContents(input)
input.focus()
pasteText(encryptedComment)
await delay(200)
if (!root.innerText.includes(encryptedComment)) return fail()
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,8 @@ import { LiveSelector, MutationObserverWatcher } from '@dimensiondev/holoflows-k
import type { SiteAdaptorUI } from '@masknet/types'
import { creator } from '../../../site-adaptor-infra/index.js'
import { getProfileIdentifierAtFacebook, getUserID } from '../utils/getProfileIdentifier.js'
import { isMobileFacebook } from '../utils/isMobile.js'
import { ProfileIdentifier, EnhanceableSite, type ValueRef } from '@masknet/shared-base'
import { searchFacebookAvatarSelector, searchUserIdOnMobileSelector } from '../utils/selector.js'
import { searchFacebookAvatarSelector } from '../utils/selector.js'
import { getAvatar, getBioDescription, getFacebookId, getNickName, getPersonalHomepage } from '../utils/user.js'
import { delay } from '@masknet/kit'
import type { IdentityResolved } from '@masknet/plugin-infra'
Expand Down Expand Up @@ -47,8 +46,6 @@ function resolveCurrentVisitingIdentityInner(
ownerRef: SiteAdaptorUI.CollectingCapabilities.IdentityResolveProvider['recognized'],
cancel: AbortSignal,
) {
const selector = isMobileFacebook ? searchUserIdOnMobileSelector() : searchFacebookAvatarSelector()

const assign = async () => {
await delay(3000)
const nickname = getNickName()
Expand Down Expand Up @@ -86,7 +83,7 @@ function resolveCurrentVisitingIdentityInner(

assign()

createWatcher(selector)
createWatcher(searchFacebookAvatarSelector())
}

export const CurrentVisitingIdentityProviderFacebook: SiteAdaptorUI.CollectingCapabilities.IdentityResolveProvider = {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,18 @@
import { None, Some, type Option } from 'ts-results-es'
import { Flags } from '@masknet/flags'
import type { SiteAdaptorUI } from '@masknet/types'
import { EnhanceableSite } from '@masknet/shared-base'
import { DOMProxy, LiveSelector, MutationObserverWatcher } from '@dimensiondev/holoflows-kit'
import { type TypedMessage, makeTypedMessageText, makeTypedMessageTuple } from '@masknet/typed-message'
import { creator } from '../../../site-adaptor-infra/utils.js'
import { isMobileFacebook } from '../utils/isMobile.js'
import { getProfileIdentifierAtFacebook } from '../utils/getProfileIdentifier.js'
import { clickSeeMore } from '../injection/PostInspector.js'
import { facebookShared } from '../shared.js'
import { createRefsForCreatePostContext } from '../../../site-adaptor-infra/utils/create-post-context.js'
import { collectNodeText } from '../../../utils/index.js'
import { startWatch } from '../../../utils/startWatch.js'
import { EnhanceableSite } from '@masknet/shared-base'

const posts = new LiveSelector().querySelectorAll<HTMLDivElement>(
isMobileFacebook ? '.story_body_container > div' : '[role=article] [id] span[dir="auto"]',
)
const posts = new LiveSelector().querySelectorAll<HTMLDivElement>('[role=article] [id] span[dir="auto"]')

export const PostProviderFacebook: SiteAdaptorUI.CollectingCapabilities.PostsProvider = {
posts: creator.EmptyPostProviderState(),
Expand Down Expand Up @@ -47,32 +44,17 @@ function collectPostsFacebookInner(
rootProxy.realCurrent = root.evaluate()[0] as HTMLElement

// ? inject after comments
const commentSelectorPC = root
const commentsSelector = root
.clone()
.querySelectorAll('[role=article] [id] span[dir="auto"]')
.closest<HTMLElement>(3)
const commentSelectorMobile = root
.clone()
.map((x) => x.parentElement)
.querySelectorAll<HTMLElement>('[data-commentid]')

const commentsSelector = isMobileFacebook ? commentSelectorMobile : commentSelectorPC

// ? inject comment text field
const commentBoxSelectorPC = root
const commentBoxSelector = root
.clone()
.querySelectorAll<HTMLFormElement>('[role="article"] [role="presentation"]:not(img)')
.map((x) => x.parentElement)

const commentBoxSelectorMobile = root
.clone()
.map((x) => x.parentElement)
.querySelectorAll('textarea')
.map((x) => x.parentElement)
.filter((x) => x.innerHTML.includes('comment'))

const commentBoxSelector = isMobileFacebook ? commentBoxSelectorMobile : commentBoxSelectorPC

const { subscriptions, ...info } = createRefsForCreatePostContext()
const postInfo = facebookShared.utils.createPostContext({
site: EnhanceableSite.Facebook,
Expand Down Expand Up @@ -144,83 +126,61 @@ function collectPostsFacebookInner(

function getPostBy(node: DOMProxy, allowCollectInfo: boolean) {
if (node.destroyed) return
const dom =
isMobileFacebook ?
node.current.querySelectorAll('a')
: [(node.current.closest('[role="article"]') ?? node.current.parentElement)!.querySelectorAll('a')[1]]
const dom = [(node.current.closest('[role="article"]') ?? node.current.parentElement)!.querySelectorAll('a')[1]]
// side effect: save to service
return getProfileIdentifierAtFacebook(Array.from(dom), allowCollectInfo)
}

function getPostID(node: DOMProxy, root: HTMLElement): null | string {
if (node.destroyed) return null
if (isMobileFacebook) {
const abbr = node.current.querySelector('abbr')
if (!abbr) return null
const idElement = abbr.closest('a')
if (!idElement) return null
const id = new URL(idElement.href)
return id.searchParams.get('id') || ''
// In single url
if (location.href.match(/plugins.+(perma.+story_fbid%3D|posts%2F)?/)) {
const url = new URL(location.href)
return url.searchParams.get('id')
} else {
// In single url
if (location.href.match(/plugins.+(perma.+story_fbid%3D|posts%2F)?/)) {
const url = new URL(location.href)
return url.searchParams.get('id')
} else {
try {
// In timeline
const postTimeNode1 = root.closest('[role=article]')?.querySelector('[href*="permalink"]')
const postIdMode1 =
postTimeNode1 ?
postTimeNode1
.getAttribute('href')
?.match(/story_fbid=(\d+)/g)?.[0]
.split('=')[1] ?? null
: null

if (postIdMode1) return postIdMode1

const postTimeNode2 = root.closest('[role=article]')?.querySelector('[href*="posts"]')
const postIdMode2 =
postTimeNode2 ?
postTimeNode2
.getAttribute('href')
?.match(/posts\/(\w+)/g)?.[0]
.split('/')[1] ?? null
: null
if (postIdMode2 && /^-?\w+$/.test(postIdMode2)) return postIdMode2
} catch {
return null
}

const parent = node.current.parentElement
if (!parent) return null
const idNode = Array.from(parent.querySelectorAll('[id]'))
.map((x) => x.id.split(';'))
.filter((x) => x.length > 1)
if (!idNode.length) return null
return idNode[0][2]
try {
// In timeline
const postTimeNode1 = root.closest('[role=article]')?.querySelector('[href*="permalink"]')
const postIdMode1 =
postTimeNode1 ?
postTimeNode1
.getAttribute('href')
?.match(/story_fbid=(\d+)/g)?.[0]
.split('=')[1] ?? null
: null

if (postIdMode1) return postIdMode1

const postTimeNode2 = root.closest('[role=article]')?.querySelector('[href*="posts"]')
const postIdMode2 =
postTimeNode2 ?
postTimeNode2
.getAttribute('href')
?.match(/posts\/(\w+)/g)?.[0]
.split('/')[1] ?? null
: null
if (postIdMode2 && /^-?\w+$/.test(postIdMode2)) return postIdMode2
} catch {
return null
}

const parent = node.current.parentElement
if (!parent) return null
const idNode = Array.from(parent.querySelectorAll('[id]'))
.map((x) => x.id.split(';'))
.filter((x) => x.length > 1)
if (!idNode.length) return null
return idNode[0][2]
}
}

function getMetadataImages(node: DOMProxy): string[] {
if (node.destroyed) return []
const parent = node.current.parentElement?.parentElement?.parentElement?.parentElement?.parentElement
if (!parent) return []
const imgNodes =
isMobileFacebook ?
parent.querySelectorAll<HTMLImageElement>('div>div>div>a>div>div>i.img')
: parent.querySelectorAll('img') || []
const imgNodes = parent.querySelectorAll('img') || []
if (!imgNodes.length) return []
const imgUrls =
isMobileFacebook ?
(getComputedStyle(imgNodes[0]).backgroundImage || '')
.slice(4, -1)
.replaceAll(/["']/g, '')
.split(',')
.filter(Boolean)
: Array.from(imgNodes, (node) => node.src).filter(Boolean)
const imgUrls = Array.from(imgNodes, (node) => node.src).filter(Boolean)
if (!imgUrls.length) return []
return imgUrls
}

0 comments on commit 11f7c46

Please sign in to comment.