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

refactor: cleanup post context #6089

Merged
merged 1 commit into from
Apr 15, 2022
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
2 changes: 1 addition & 1 deletion packages/encryption/src/encryption/DecryptionTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ export interface DecryptReportedInfo {
claimedAuthor?: ProfileIdentifier
publicShared?: boolean
version?: SupportedPayloadVersions
ownersKeyEncrypted?: Uint8Array
isAuthorOfPost?: boolean
}
export interface DecryptIntermediateProgress {
type: DecryptProgressKind.Progress
Expand Down
3 changes: 1 addition & 2 deletions packages/mask/background/services/crypto/decryption.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,7 @@ async function* decryption(payload: string | Uint8Array, context: DecryptionCont
if (parse.val.encryption.ok) {
const val = parse.val.encryption.val
info.publicShared = val.type === 'public'
if (val.type === 'E2E' && val.ownersAESKeyEncrypted.ok)
info.ownersKeyEncrypted = val.ownersAESKeyEncrypted.val
if (val.type === 'E2E') info.isAuthorOfPost = val.ownersAESKeyEncrypted.ok
}
yield info
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,13 @@
import { Fragment, useEffect, useMemo, useReducer } from 'react'
import {
extractTextFromTypedMessage,
makeTypedMessageTuple,
TypedMessage,
TypedMessageTuple,
} from '@masknet/typed-message'
import { extractTextFromTypedMessage, TypedMessage } from '@masknet/typed-message'
import type { ProfileIdentifier } from '@masknet/shared-base'

import { ServicesWithProgress } from '../../../extension/service'
import Services, { ServicesWithProgress } from '../../../extension/service'
import type { Profile } from '../../../database'
import type { DecryptionProgress, FailureDecryption, SuccessDecryption } from './types'
import { DecryptPostSuccess, DecryptPostSuccessProps } from './DecryptedPostSuccess'
import { DecryptPostAwaiting, DecryptPostAwaitingProps } from './DecryptPostAwaiting'
import { DecryptPostFailed, DecryptPostFailedProps } from './DecryptPostFailed'
import { DecryptPostSuccess } from './DecryptedPostSuccess'
import { DecryptPostAwaiting } from './DecryptPostAwaiting'
import { DecryptPostFailed } from './DecryptPostFailed'
import { usePostClaimedAuthor } from '../../DataSource/usePostInfo'
import { delay, encodeArrayBuffer, safeUnreachable } from '@dimensiondev/kit'
import { activatedSocialNetworkUI } from '../../../social-network'
Expand Down Expand Up @@ -50,30 +45,21 @@ function progressReducer(
}

export interface DecryptPostProps {
onDecrypted: (post: TypedMessageTuple) => void
whoAmI: ProfileIdentifier
profiles: Profile[]
alreadySelectedPreviously: Profile[]
requestAppendRecipients?(to: Profile[]): Promise<void>
successComponent?: React.ComponentType<DecryptPostSuccessProps>
successComponentProps?: Partial<DecryptPostSuccessProps>
waitingComponent?: React.ComponentType<DecryptPostAwaitingProps>
waitingComponentProps?: Partial<DecryptPostAwaitingProps>
failedComponent?: React.ComponentType<DecryptPostFailedProps>
failedComponentProps?: Partial<DecryptPostFailedProps>
}
export function DecryptPost(props: DecryptPostProps) {
const { whoAmI, profiles, alreadySelectedPreviously, onDecrypted } = props
const { whoAmI, profiles, alreadySelectedPreviously } = props
const deconstructedPayload = usePostInfoDetails.containingMaskPayload()
const authorInPayload = usePostClaimedAuthor()
const current = usePostInfo()!
const currentPostBy = usePostInfoDetails.author()
const postBy = authorInPayload || currentPostBy
const postMetadataImages = usePostInfoDetails.postMetadataImages()
const mentionedLinks = usePostInfoDetails.mentionedLinks()
const Success = props.successComponent || DecryptPostSuccess
const Awaiting = props.waitingComponent || DecryptPostAwaiting
const Failed = props.failedComponent || DecryptPostFailed
const postInfo = usePostInfo()

const requestAppendRecipientsWrapped = useMemo(() => {
if (!postBy.equals(whoAmI)) return undefined
Expand All @@ -95,6 +81,12 @@ export function DecryptPost(props: DecryptPostProps) {
const sharedPublic = usePostInfoDetails.publicShared()

useEffect(() => {
function setCommentFns(iv: Uint8Array, message: TypedMessage) {
postInfo!.encryptComment.value = async (comment) =>
Services.Crypto.encryptComment(iv, extractTextFromTypedMessage(message).unwrap(), comment)
postInfo!.decryptComment.value = async (encryptedComment) =>
Services.Crypto.decryptComment(iv, extractTextFromTypedMessage(message).unwrap(), encryptedComment)
}
const signal = new AbortController()
const postURL = current.url.getCurrentValue()?.toString()
const report =
Expand Down Expand Up @@ -127,6 +119,7 @@ export function DecryptPost(props: DecryptPostProps) {
mentionedLinks.join(' '),
},
(message, iv) => {
setCommentFns(iv, message)
dispatch({
type: 'refresh',
key: 'text',
Expand All @@ -151,6 +144,7 @@ export function DecryptPost(props: DecryptPostProps) {
whoAmI,
{ type: 'image-url', image: url },
(message, iv) => {
setCommentFns(iv, message)
dispatch({
type: 'refresh',
key: url,
Expand All @@ -170,15 +164,6 @@ export function DecryptPost(props: DecryptPostProps) {
return () => signal.abort()
}, [deconstructedPayload.ok, postBy.toText(), postMetadataImages.join(), whoAmI.toText(), mentionedLinks.join()])

// pass 3:
// invoke callback
const firstSucceedDecrypted = progress.find((p) => p.progress.type === 'success')
useEffect(() => {
if (firstSucceedDecrypted?.progress.type !== 'success') return
onDecrypted(makeTypedMessageTuple([firstSucceedDecrypted.progress.content]))
}, [firstSucceedDecrypted, onDecrypted])
// #endregion

// it's not a secret post
if (!deconstructedPayload.ok && progress.every((x) => x.progress.internal)) return null
return (
Expand All @@ -196,35 +181,26 @@ export function DecryptPost(props: DecryptPostProps) {
switch (progress.type) {
case 'success':
return (
<Success
<DecryptPostSuccess
data={progress}
alreadySelectedPreviously={alreadySelectedPreviously}
requestAppendRecipients={requestAppendRecipientsWrapped}
profiles={profiles}
sharedPublic={sharedPublic}
author={authorInPayload}
postedBy={currentPostBy}
{...props.successComponentProps}
/>
)
case 'error':
return (
<Failed
<DecryptPostFailed
error={new Error(progress.error)}
author={authorInPayload}
postedBy={currentPostBy}
{...props.failedComponentProps}
/>
)
case 'progress':
return (
<Awaiting
type={progress}
author={authorInPayload}
postedBy={currentPostBy}
{...props.waitingComponentProps}
/>
)
return <DecryptPostAwaiting type={progress} author={authorInPayload} postedBy={currentPostBy} />
default:
return null
}
Expand Down Expand Up @@ -255,8 +231,8 @@ async function makeProgress(
done(progress.content, iv || new Uint8Array())
} else if (progress.type === DecryptProgressKind.Info) {
iv ??= progress.iv
if (progress.ownersKeyEncrypted)
reporter({ ownersAESKeyEncrypted: encodeArrayBuffer(progress.ownersKeyEncrypted) })
if (typeof progress.isAuthorOfPost === 'boolean')
reporter({ isAuthorOfPost: Some(progress.isAuthorOfPost) })
if (progress.iv) reporter({ iv: encodeArrayBuffer(progress.iv) })
if (progress.version) reporter({ version: progress.version })
if (typeof progress.publicShared === 'boolean') reporter({ sharedPublic: Some(progress.publicShared) })
Expand Down
22 changes: 4 additions & 18 deletions packages/mask/src/components/InjectedComponents/PostComments.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,7 @@ import type { ChipProps } from '@mui/material/Chip'
import Lock from '@mui/icons-material/Lock'
import { useEffect } from 'react'
import { useAsync } from 'react-use'
import Services from '../../extension/service'
import { extractTextFromTypedMessage } from '@masknet/typed-message'
import { usePostInfoDetails } from '@masknet/plugin-infra/content-script'
import { decodeArrayBuffer } from '@dimensiondev/kit'

const useStyle = makeStyles()({
root: {
Expand Down Expand Up @@ -47,22 +44,11 @@ export interface PostCommentProps {
export function PostComment(props: PostCommentProps) {
const { needZip } = props
const comment = useValueRef(props.comment)
const postContent = usePostInfoDetails.rawMessagePiped()
const iv = usePostInfoDetails.iv()
const decrypt = usePostInfoDetails.decryptComment()

const dec = useAsync(async () => {
const decryptedText = extractTextFromTypedMessage(postContent).unwrap()
if (!iv || !decryptedText) throw new Error('Decrypt comment failed')
const result = await Services.Crypto.decryptComment(
new Uint8Array(decodeArrayBuffer(iv)),
decryptedText,
comment,
)
if (result === null) throw new Error('Decrypt result empty')
return result
}, [iv, postContent, comment])
const { value } = useAsync(async () => decrypt?.(comment), [decrypt, comment])

useEffect(() => void (dec.value && needZip()), [dec.value, needZip])
if (dec.value) return <PostCommentDecrypted>{dec.value}</PostCommentDecrypted>
useEffect(() => void (value && needZip()), [value, needZip])
if (value) return <PostCommentDecrypted>{value}</PostCommentDecrypted>
return null
}
19 changes: 8 additions & 11 deletions packages/mask/src/components/InjectedComponents/PostInspector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@ import { useState, useEffect } from 'react'
import { useAsync } from 'react-use'
import { DecryptPost } from './DecryptedPost/DecryptedPost'
import Services from '../../extension/service'
import { PostIVIdentifier, ProfileIdentifier } from '@masknet/shared-base'
import type { TypedMessageTuple } from '@masknet/typed-message'
import { ProfileIdentifier } from '@masknet/shared-base'
import type { Profile } from '../../database'
import { useCurrentIdentity, useFriendsList } from '../DataSource/useActivatedUI'
import {
Expand All @@ -23,17 +22,16 @@ const PluginHooksRenderer = createInjectHooksRenderer(
)

export interface PostInspectorProps {
onDecrypted(post: TypedMessageTuple): void
needZip(): void
zipPost(): void
/** @default 'before' */
slotPosition?: 'before' | 'after'
}
export function PostInspector(props: PostInspectorProps) {
const postBy = usePostInfoDetails.author()
const encryptedPost = usePostInfoDetails.containingMaskPayload()
const ownersKeyEncrypted = usePostInfoDetails.ownersKeyEncrypted()
const isAuthorOfPost = usePostInfoDetails.isAuthorOfPost()
const version = usePostInfoDetails.version()
const iv = usePostInfoDetails.iv()
const iv = usePostInfoDetails.postIVIdentifier()
const postImages = usePostInfoDetails.postMetadataImages()
const isDebugging = useSubscription(PersistentStorages.Settings.storage.debugging.subscription)
const whoAmI = useCurrentIdentity()
Expand All @@ -42,24 +40,23 @@ export function PostInspector(props: PostInspectorProps) {

const { value: sharedListOfPost } = useAsync(async () => {
if (!whoAmI || !whoAmI.identifier.equals(postBy) || !iv) return []
return Services.Crypto.getPartialSharedListOfPost(new PostIVIdentifier(whoAmI.identifier.network, iv))
return Services.Crypto.getPartialSharedListOfPost(iv)
}, [postBy, whoAmI, iv])
useEffect(() => setAlreadySelectedPreviously(sharedListOfPost ?? []), [sharedListOfPost])

if (encryptedPost.ok || postImages.length) {
if (!isDebugging) props.needZip()
if (!isDebugging) props.zipPost()
return withAdditionalContent(
<DecryptPost
onDecrypted={props.onDecrypted}
requestAppendRecipients={
// version -40 does not support append receiver
// version -37 is not implemented yet.
ownersKeyEncrypted && iv && version && version === -38
isAuthorOfPost && iv && version && version === -38
? async (profile) => {
setAlreadySelectedPreviously(alreadySelectedPreviously.concat(profile))
return Services.Crypto.appendShareTarget(
version,
new PostIVIdentifier(whoAmI!.identifier.network, iv),
iv,
profile.map((x) => x.identifier),
whoAmI!.identifier,
{ type: 'direct', at: new Date() },
Expand Down
21 changes: 6 additions & 15 deletions packages/mask/src/social-network/defaults/inject/CommentBox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,11 @@ import { memo, useCallback } from 'react'
import { type PostInfo, usePostInfoDetails, usePostInfo, PostInfoProvider } from '@masknet/plugin-infra/content-script'
import { DOMProxy, MutationObserverWatcher } from '@dimensiondev/holoflows-kit'
import { CommentBox, CommentBoxProps } from '../../../components/InjectedComponents/CommentBox'
import Services from '../../../extension/service'
import { createReactRootShadowed } from '../../../utils/shadow-root/renderInShadowRoot'
import { makeStyles } from '@masknet/theme'
import { noop } from 'lodash-unified'
import { MaskMessages } from '../../../utils/messages'
import { startWatch } from '../../../utils/watcher'
import { extractTextFromTypedMessage } from '@masknet/typed-message'
import { decodeArrayBuffer } from '@dimensiondev/kit'

const defaultOnPasteToCommentBox = async (
encryptedComment: string,
Expand All @@ -29,25 +26,19 @@ export const injectCommentBoxDefaultFactory = function <T extends string>(
) {
const CommentBoxUI = memo(function CommentBoxUI({ dom }: { dom: HTMLElement | null }) {
const info = usePostInfo()
const postContent = usePostInfoDetails.rawMessagePiped()
const encryptComment = usePostInfoDetails.encryptComment()
const { classes } = useCustomStyles()
const iv = usePostInfoDetails.iv()
const props = additionPropsToCommentBox(classes)
const onCallback = useCallback(
async (content: string) => {
const decryptedText = extractTextFromTypedMessage(postContent).unwrap()
const encryptedComment = await Services.Crypto.encryptComment(
new Uint8Array(decodeArrayBuffer(iv!)),
decryptedText,
content,
)
onPasteToCommentBox(encryptedComment, info!, dom).catch(console.error)
if (!encryptComment) return
const encryptedComment = await encryptComment(content)
onPasteToCommentBox(encryptedComment, info!, dom)
},
[postContent, info, dom, iv],
[encryptComment, info, dom],
)

if (!iv) return null
if (!postContent.items.length) return null
if (!encryptComment) return null
return <CommentBox onSubmit={onCallback} {...props} />
})
return (signal: AbortSignal, current: PostInfo) => {
Expand Down
17 changes: 4 additions & 13 deletions packages/mask/src/social-network/defaults/inject/PostInspector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,28 +11,19 @@ export function injectPostInspectorDefault<T extends string>(
additionalPropsToPostInspector: (classes: Record<T, string>) => Partial<PostInspectorProps> = () => ({}),
useCustomStyles: (props?: any) => { classes: Record<T, string> } = makeStyles()({}) as any,
) {
const PostInspectorDefault = memo(function PostInspectorDefault(props: {
onDecrypted: PostInspectorProps['onDecrypted']
zipPost: PostInspectorProps['needZip']
}) {
const { onDecrypted, zipPost } = props
const PostInspectorDefault = memo(function PostInspectorDefault(props: Pick<PostInspectorProps, 'zipPost'>) {
const { zipPost } = props
const { classes } = useCustomStyles()
const additionalProps = additionalPropsToPostInspector(classes)
return <PostInspector onDecrypted={onDecrypted} needZip={zipPost} {...additionalProps} />
return <PostInspector zipPost={zipPost} {...additionalProps} />
})

const { zipPost, injectionPoint } = config
const zipPostF = zipPost || noop
return function injectPostInspector(current: PostInfo, signal: AbortSignal) {
const jsx = (
<PostInfoProvider post={current}>
<PostInspectorDefault
onDecrypted={(typed) => {
current.rawMessagePiped.value = typed
}}
zipPost={() => zipPostF(current.rootElement)}
{...current}
/>
<PostInspectorDefault zipPost={() => zipPostF(current.rootElement)} />
</PostInfoProvider>
)
const root = createReactRootShadowed(injectionPoint?.(current) ?? current.rootElement.afterShadow, {
Expand Down
Loading