Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: mf-6533 hide decrypt payload image #12021

Merged
merged 1 commit into from
Jan 6, 2025
Merged
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
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,20 @@ 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)
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 +63,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
Loading