Skip to content

Commit

Permalink
feat: mf-6533 hide decrypt payload image
Browse files Browse the repository at this point in the history
  • Loading branch information
swkatmask committed Jan 6, 2025
1 parent 3f612d0 commit bac1863
Show file tree
Hide file tree
Showing 7 changed files with 94 additions and 50 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ function progressReducer(

interface DecryptPostProps {
whoAmI: ProfileIdentifier | null
imageDecryptedResults: Record<string, boolean>
onImageDecrypted: (decryptedResults: Record<string, boolean>) => void
}
function isProgressEqual(a: PossibleProgress, b: PossibleProgress) {
if (a.type !== b.type) return false
Expand All @@ -60,8 +62,7 @@ function isProgressEqual(a: PossibleProgress, b: PossibleProgress) {
safeUnreachable(a)
return false
}
export function DecryptPost(props: DecryptPostProps) {
const { whoAmI } = props
export function DecryptPost({ whoAmI, imageDecryptedResults, onImageDecrypted }: DecryptPostProps) {
const deconstructedPayload = usePostInfoDetails.hasMaskPayload()
const currentPostBy = usePostInfoDetails.author()
// TODO: we should read this from the payload.
Expand Down Expand Up @@ -148,6 +149,11 @@ export function DecryptPost(props: DecryptPostProps) {
iv: encodeArrayBuffer(iv),
},
})
onImageDecrypted({
...imageDecryptedResults,
// For now, we only want to know if an image has been decrypted.
[url]: true,
})
},
postInfo.decryptedReport,
report(url),
Expand All @@ -168,7 +174,7 @@ export function DecryptPost(props: DecryptPostProps) {
{uniqWith(progress, (a, b) => isProgressEqual(a.progress, b.progress))
// the internal progress should not display to the end-user
.filter(({ progress }) => !progress.internal)
.map(({ progress, key }, index) => (
.map(({ progress, key }) => (
<Fragment key={key}>{renderProgress(progress)}</Fragment>
))}
</>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,29 +1,29 @@
import { memo, useContext, useEffect, useState } from 'react'
import { attachNextIDToProfile } from '../../../../shared-ui/index.js'
import { AdditionalContent } from '../AdditionalPostContent.js'
import { SelectProfileDialog } from '../SelectPeopleDialog.js'
import { makeStyles } from '@masknet/theme'
import { Typography, useTheme } from '@mui/material'
import type { TypedMessage } from '@masknet/typed-message'
import Services from '#services'
import { Trans } from '@lingui/macro'
import { Icons } from '@masknet/icons'
import { delay } from '@masknet/kit'
import { PostInfoContext, usePostInfoDetails, usePostInfoPostIVIdentifier } from '@masknet/plugin-infra/content-script'
import {
EMPTY_LIST,
MaskMessages,
type ProfileIdentifier,
type ProfileInformation,
type ProfileInformationFromNextID,
} from '@masknet/shared-base'
import { useAuthorDifferentMessage } from './authorDifferentMessage.js'
import { DecryptedUIPluginRendererWithSuggestion } from '../DecryptedPostMetadataRender.js'
import { PostInfoContext, usePostInfoDetails, usePostInfoPostIVIdentifier } from '@masknet/plugin-infra/content-script'
import { makeStyles } from '@masknet/theme'
import type { TypedMessage } from '@masknet/typed-message'
import { Typography, useTheme } from '@mui/material'
import { memo, useContext, useEffect, useState } from 'react'
import { attachNextIDToProfile } from '../../../../shared-ui/index.js'
import { activatedSiteAdaptorUI } from '../../../site-adaptor-infra/index.js'
import type { LazyRecipients } from '../../CompositionDialog/CompositionUI.js'
import { useRecipientsList } from '../../CompositionDialog/useRecipientsList.js'
import { useSelectedRecipientsList } from '../../CompositionDialog/useSelectedRecipientsList.js'
import Services from '#services'
import type { LazyRecipients } from '../../CompositionDialog/CompositionUI.js'
import { delay } from '@masknet/kit'
import { activatedSiteAdaptorUI } from '../../../site-adaptor-infra/index.js'
import { AdditionalContent } from '../AdditionalPostContent.js'
import { DecryptedUIPluginRendererWithSuggestion } from '../DecryptedPostMetadataRender.js'
import { SelectProfileDialog } from '../SelectPeopleDialog.js'
import { useAuthorDifferentMessage } from './authorDifferentMessage.js'
import { RecipientsToolTip } from './RecipientsToolTip.js'
import { Icons } from '@masknet/icons'
import { Trans } from '@lingui/macro'

interface DecryptPostSuccessProps {
message: TypedMessage
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { createInjectHooksRenderer, useActivatedPluginsSiteAdaptor } from '@masknet/plugin-infra/content-script'
import type { MetadataRenderProps } from '@masknet/typed-message-react'
import { extractTextFromTypedMessage } from '@masknet/typed-message'
import type { MetadataRenderProps } from '@masknet/typed-message-react'
import { useMemo } from 'react'
import {
PossiblePluginSuggestionUI,
useDisabledPluginSuggestionFromMeta,
Expand All @@ -17,7 +18,7 @@ const Decrypted = createInjectHooksRenderer(
export function DecryptedUIPluginRendererWithSuggestion(props: MetadataRenderProps) {
const a = useDisabledPluginSuggestionFromMeta(props.metadata)
const b = useDisabledPluginSuggestionFromPost(extractTextFromTypedMessage(props.message), [])
const suggest = Array.from(new Set(a.concat(b)))
const suggest = useMemo(() => Array.from(new Set(a.concat(b))), [a, b])

return (
<>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import { type ReactNode, useCallback } from 'react'
import { useAsync } from 'react-use'
import type { Option } from 'ts-results-es'
import { useSubscription } from 'use-subscription'
import Services from '#services'
import { Trans } from '@lingui/macro'
import { Icons } from '@masknet/icons'
import {
type Plugin,
Expand All @@ -16,8 +14,10 @@ import { BooleanPreference, EMPTY_LIST } from '@masknet/shared-base'
import { makeStyles, MaskLightTheme } from '@masknet/theme'
import { extractTextFromTypedMessage } from '@masknet/typed-message'
import { Box, type BoxProps, Button, Skeleton, Typography, useTheme } from '@mui/material'
import Services from '#services'
import { Trans } from '@lingui/macro'
import { useQuery } from '@tanstack/react-query'
import { type ReactNode, useCallback } from 'react'
import type { Option } from 'ts-results-es'
import { useSubscription } from 'use-subscription'

function useDisabledPlugins() {
const activated = new Set<string>(useActivatedPluginsSiteAdaptor('any').map((x) => x.ID))
Expand Down Expand Up @@ -95,10 +95,13 @@ export function PossiblePluginSuggestionUISingle(props: {
}
}, [lackHostPermission, define])

const { value: disabled } = useAsync(async () => {
const status = await Services.Settings.getPluginMinimalModeEnabled(define.ID)
return status === BooleanPreference.True
}, [define.ID])
const { data: disabled } = useQuery({
queryKey: ['system', 'plugin-disabled', define.ID],
queryFn: async () => {
const status = await Services.Settings.getPluginMinimalModeEnabled(define.ID)
return status === BooleanPreference.True
},
})

const ButtonIcon = lackHostPermission ? Icons.Approve : Icons.Plugin
const wrapperContent = content ?? <FallbackContent disabled={disabled} height={74} />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,24 +1,28 @@
import { useSubscription } from 'use-subscription'
import {
usePostInfoDetails,
createInjectHooksRenderer,
useActivatedPluginsSiteAdaptor,
usePostInfoDetails,
} from '@masknet/plugin-infra/content-script'
import { DecryptPost } from './DecryptedPost/DecryptedPost.js'
import { PersistentStorages } from '@masknet/shared-base'
import { useState, type JSX } from 'react'
import { useSubscription } from 'use-subscription'
import { useCurrentIdentity } from '../DataSource/useActivatedUI.js'
import { DecryptPost } from './DecryptedPost/DecryptedPost.js'
import { PossiblePluginSuggestionPostInspector } from './DisabledPluginSuggestion.js'
import { MaskPostExtraPluginWrapperWithPermission } from './PermissionBoundary.js'
import { PersistentStorages } from '@masknet/shared-base'
import type { JSX } from 'react'

const PluginHooksRenderer = createInjectHooksRenderer(
useActivatedPluginsSiteAdaptor.visibility.useNotMinimalMode,
(plugin) => plugin.PostInspector,
MaskPostExtraPluginWrapperWithPermission,
)

export interface PostPayloadContext {
imageDecryptedResults: Record<string, boolean>
}

export interface PostInspectorProps {
zipPost(): void
zipPost(payloadContext: PostPayloadContext): void
/** @default 'before' */
slotPosition?: 'before' | 'after'
}
Expand All @@ -29,9 +33,17 @@ export function PostInspector(props: PostInspectorProps) {
const isDebugging = useSubscription(PersistentStorages.Settings.storage.debugging.subscription)
const whoAmI = useCurrentIdentity()

const [imageDecryptedResults, setImageDecryptedResults] = useState<Record<string, boolean>>({})

if (hasEncryptedPost || postImages.length) {
if (!isDebugging) props.zipPost()
return withAdditionalContent(<DecryptPost whoAmI={whoAmI?.identifier || null} />)
if (!isDebugging) props.zipPost({ imageDecryptedResults })
return withAdditionalContent(
<DecryptPost
whoAmI={whoAmI?.identifier || null}
imageDecryptedResults={imageDecryptedResults}
onImageDecrypted={setImageDecryptedResults}
/>,
)
}
return withAdditionalContent(null)
function withAdditionalContent(x: JSX.Element | null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,31 @@ import { memo } from 'react'
import type { DOMProxy } from '@dimensiondev/holoflows-kit'
import { type PostInfo, PostInfoContext } from '@masknet/plugin-infra/content-script'
import { attachReactTreeWithContainer } from '../../../utils/shadow-root/renderInShadowRoot.js'
import { PostInspector, type PostInspectorProps } from '../../../components/InjectedComponents/PostInspector.js'
import {
PostInspector,
type PostInspectorProps,
type PostPayloadContext,
} from '../../../components/InjectedComponents/PostInspector.js'
import { noop } from 'lodash-es'

export function injectPostInspectorDefault(
config: InjectPostInspectorDefaultConfig = {},
props?: Pick<PostInspectorProps, 'slotPosition'>,
) {
const PostInspectorDefault = memo(function PostInspectorDefault(props: { zipPost(): void }) {
const PostInspectorDefault = memo(function PostInspectorDefault(props: Pick<PostInspectorProps, 'zipPost'>) {
return <PostInspector slotPosition="after" {...props} />
})

const { zipPost, injectionPoint } = config
const zipPostF = zipPost || noop

return function injectPostInspector(current: PostInfo, signal: AbortSignal) {
return function injectPostInspector(postInfo: PostInfo, signal: AbortSignal) {
const jsx = (
<PostInfoContext value={current}>
<PostInspectorDefault {...props} zipPost={() => zipPostF(current.rootElement)} />
<PostInfoContext value={postInfo}>
<PostInspectorDefault {...props} zipPost={(context) => zipPostF(postInfo.rootElement, context)} />
</PostInfoContext>
)
const root = attachReactTreeWithContainer(injectionPoint?.(current) ?? current.rootElement.afterShadow, {
const root = attachReactTreeWithContainer(injectionPoint?.(postInfo) ?? postInfo.rootElement.afterShadow, {
key: 'post-inspector',
untilVisible: true,
signal,
Expand All @@ -33,6 +37,6 @@ export function injectPostInspectorDefault(
}

interface InjectPostInspectorDefaultConfig {
zipPost?(node: DOMProxy): void
zipPost?(node: DOMProxy, postPayloadContext?: PostPayloadContext): void
injectionPoint?: (postInfo: PostInfo) => ShadowRoot
}
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
/* eslint @masknet/unicode-specific-set: ["error", { "only": "code" }] */
import { TwitterDecoder } from '@masknet/encryption'
import type { PostInfo } from '@masknet/plugin-infra/content-script'
import { injectPostInspectorDefault } from '../../../site-adaptor-infra/defaults/inject/PostInspector.js'
import { getOrAttachShadowRoot } from '@masknet/shared-base-ui'
import { isEmpty } from 'lodash-es'
import { injectPostInspectorDefault } from '../../../site-adaptor-infra/defaults/inject/PostInspector.js'

export function injectPostInspectorAtTwitter(signal: AbortSignal, current: PostInfo) {
return injectPostInspectorDefault({
const inject = injectPostInspectorDefault({
injectionPoint(postInfo) {
if (postInfo.rootElement.realCurrent!.dataset.testid === 'tweetPhoto') {
const root = postInfo.rootElement.realCurrent!.closest('div[aria-labelledby]') as HTMLDivElement
return getOrAttachShadowRoot(root)
}
return postInfo.rootElement.afterShadow
},
zipPost(node) {
zipPost(node, payloadContext) {
if (node.destroyed) return
const contentContainer = node.current.parentElement
if (!contentContainer) return
Expand All @@ -34,6 +35,22 @@ export function injectPostInspectorAtTwitter(signal: AbortSignal, current: PostI
// match (any space) (*/) (any space)
if (span.innerText.match(/^ +\*\/ ?$/)) hideDOM(span)
}
const article = contentContainer.closest('article')
if (article && !isEmpty(payloadContext?.imageDecryptedResults)) {
for (const img of article.querySelectorAll('img')) {
if (!payloadContext.imageDecryptedResults[img.src]) continue
const a = img.closest<HTMLElement>('a[href*="/photo/"][role=link]')
if (!a) continue
hideDOM(a)
Promise.resolve().then(() => {
const wrapper = a.closest<HTMLElement>('div[aria-labelledby]')
if (wrapper?.textContent === '') {
wrapper.style.display = 'none'
wrapper.setAttribute('aria-hidden', 'true')
}
})
}
}

const parent = content.parentElement?.nextElementSibling as HTMLElement
if (parent && matches(parent.innerText)) {
Expand All @@ -48,7 +65,8 @@ export function injectPostInspectorAtTwitter(signal: AbortSignal, current: PostI
cardWrapper.setAttribute('aria-hidden', 'true')
}
},
})(current, signal)
})
return inject(current, signal)
}
function matches(input: string) {
input = input.toLowerCase()
Expand Down

0 comments on commit bac1863

Please sign in to comment.