Skip to content

Commit

Permalink
feat: add encrypted comment shipped
Browse files Browse the repository at this point in the history
  • Loading branch information
Jack-Works committed Jul 19, 2019
1 parent 5e38c87 commit 339f420
Show file tree
Hide file tree
Showing 7 changed files with 102 additions and 24 deletions.
29 changes: 29 additions & 0 deletions src/components/InjectedComponents/CommentBox.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import React, { useEffect, useState } from 'react'
import { styled } from '@material-ui/core/styles'
import { useCapturedInput } from '../../utils/hooks/useCapturedEvents'

const Input = styled('input')({
background: '#f2f3f5',
border: '1px solid #ccd0d5',
width: '100%',
height: 34,
borderRadius: 20,
padding: '2px 1em',
boxSizing: 'border-box',
marginTop: 6,
})
export function CommentBox(props: { onSubmit(newVal: string): void; display: boolean }) {
const ref = React.createRef<HTMLInputElement>()
const binder = useCapturedInput(ref, () => {})
useEffect(
binder(['keypress'], e => {
if (!ref.current) return
if (e.key === 'Enter') {
props.onSubmit(ref.current.value)
ref.current.value = ''
}
}),
)
if (!props.display) return null
return <Input ref={ref} placeholder="Maskbook!" />
}
5 changes: 3 additions & 2 deletions src/components/InjectedComponents/PostComments.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,16 @@ interface Props {
postIV: string
postContent: string
comment: string
needZip(): void
}
export function PostComment({ comment, postContent, postIV }: Props) {
export function PostComment({ comment, postContent, postIV, needZip }: Props) {
return (
<AsyncComponent
promise={() => Services.Crypto.decryptComment(postIV, postContent, comment)}
dependencies={[postIV, postContent, comment]}
awaitingComponent={null}
completeComponent={result =>
result.data ? <PostCommentDecrypted>{result.data}</PostCommentDecrypted> : null
result.data ? (needZip(), <PostCommentDecrypted>{result.data}</PostCommentDecrypted>) : null
}
failedComponent={null}
/>
Expand Down
16 changes: 10 additions & 6 deletions src/crypto/crypto-alpha-40.ts
Original file line number Diff line number Diff line change
Expand Up @@ -333,11 +333,15 @@ export async function decryptComment(
const payload = extractCommentPayload(encryptComment)
if (!payload) return
const key = await getCommentKey(postIV, postContent)
const x = await decryptWithAES({
aesKey: key,
iv: decodeArrayBuffer(postIV),
encrypted: payload,
})
return decodeText(x)
try {
const x = await decryptWithAES({
aesKey: key,
iv: decodeArrayBuffer(postIV),
encrypted: payload,
})
return decodeText(x)
} catch {
return undefined
}
}
//#endregion
67 changes: 54 additions & 13 deletions src/extension/content-script/injections/Posts.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from 'react'
import React, { useCallback } from 'react'
import { LiveSelector, MutationObserverWatcher, DomProxy, ValueRef } from '@holoflows/kit'
import { getPersonIdentifierAtFacebook } from './MyUsername'
import { renderInShadowRoot } from '../../../utils/jss/renderInShadowRoot'
Expand All @@ -8,6 +8,9 @@ import { isMobile } from '../../../social-network/facebook.com/isMobile'
import { PostCommentDecrypted, PostComment } from '../../../components/InjectedComponents/PostComments'
import { useValueRef } from '../../../utils/hooks/useValueRef'
import { PostInspector } from './Posts/PostInspector'
import { CommentBox } from '../../../components/InjectedComponents/CommentBox'
import { selectElementContents, dispatchCustomEvents, sleep } from '../../../utils/utils'
import Services from '../../service'

const posts = new LiveSelector().querySelectorAll<HTMLDivElement>(
isMobile ? '.story_body_container ' : '.userContent, .userContent+*+div>div>div>div>div',
Expand All @@ -17,6 +20,7 @@ type InspectedPostInfo = {
postBy: ValueRef<PersonIdentifier>
postID: ValueRef<string | null>
postContent: ValueRef<string>
postPayload: ValueRef<ReturnType<typeof deconstructPayload> | null>
comments: Set<HTMLElement>
decryptedPostContent: ValueRef<string>
}
Expand All @@ -30,39 +34,45 @@ new MutationObserverWatcher(posts)
postBy: new ValueRef(PersonIdentifier.unknown),
postContent: new ValueRef(''),
postID: new ValueRef(''),
postPayload: new ValueRef(null),
}
inspectedPosts.add(info)

function collectPostInfo() {
info.postContent.value = node.current.innerText
info.postBy.value = getPostBy(node, deconstructPayload(info.postContent.value) !== null)
info.postPayload.value = deconstructPayload(info.postContent.value)
info.postBy.value = getPostBy(node, info.postPayload.value !== null)
info.postID.value = getPostID(node)
}
collectPostInfo()
clickSeeMore(node)

const root = new LiveSelector().replace(() => [node.current]).closest('.userContentWrapper')
// ? inject after comments
const commentSelector = root
.clone()
.querySelectorAll('[role=article]')
.querySelectorAll('a+span')
.closest<HTMLElement>(3)
console.log(inspectedPosts)
.closest<HTMLElement>(2)
const commentWatcher = new MutationObserverWatcher(commentSelector, root.evaluateOnce()[0])
.useForeach(commentNode => {
const commentRef = new ValueRef(commentNode.current.innerText)
const UI = () => {
const CommentUI = () => {
const postContent = useValueRef(info.decryptedPostContent)
const postIV = useValueRef(info.postContent)
const comment = useValueRef(commentRef)
return (
<PostComment
needZip={useCallback(() => {
commentNode.current.style.whiteSpace = 'nowrap'
commentNode.current.style.overflow = 'hidden'
}, [])}
comment={comment}
postContent={postContent}
postIV={(deconstructPayload(postIV) || { iv: '' }).iv}
postIV={info.postPayload.value ? info.postPayload.value.iv : ''}
/>
)
}
const unmount = renderInShadowRoot(<UI />, commentNode.afterShadow)
const unmount = renderInShadowRoot(<CommentUI />, commentNode.afterShadow)
return {
onNodeMutation() {
commentRef.value = commentNode.current.innerText
Expand All @@ -75,15 +85,45 @@ new MutationObserverWatcher(posts)
},
}
})
.setDomProxyOption({ afterShadowRootInit: { mode: 'closed' } })
.startWatch()

clickSeeMore(node)
// ? inject comment text field
const commentBoxSelector = root
.clone()
.querySelector<HTMLFormElement>('form form')
.enableSingleMode()
const commentBoxWatcher = new MutationObserverWatcher(commentBoxSelector, root.evaluateOnce()[0])
.setDomProxyOption({ afterShadowRootInit: { mode: 'closed' } })
.enableSingleMode()
.startWatch()
const CommentBoxUI = () => {
const payload = useValueRef(info.postPayload)
const decrypted = useValueRef(info.decryptedPostContent)
return (
<CommentBox
display={!!(payload && decrypted)}
onSubmit={async content => {
const encryptedComment = await Services.Crypto.encryptComment(payload!.iv, decrypted, content)
const _root = root.evaluateOnce()[0] as HTMLDivElement
selectElementContents(_root.querySelector('[contenteditable]')!)
dispatchCustomEvents('paste', encryptedComment)
await sleep(200)
if (_root.innerText.match(encryptedComment)) console.log('Okay')
else prompt('Please paste it into the comment box!', encryptedComment)
}}
/>
)
}
const unmountCommentBox = renderInShadowRoot(<CommentBoxUI />, commentBoxWatcher.firstVirtualNode.afterShadow)

// ? inject after posts
const zipPost = () => {
zipEncryptedPostContent(node)
zipPostLinkPreview(node)
}
const onDecrypted = (val: string) => (info.decryptedPostContent.value = val)
const UI = () => {
const PostDecryptUI = () => {
const id = useValueRef(info.postID)
const by = useValueRef(info.postBy)
const content = useValueRef(info.postContent)
Expand All @@ -98,18 +138,19 @@ new MutationObserverWatcher(posts)
)
}
// Render it
const unmount = renderInShadowRoot(<UI />, node.afterShadow)
const unmountPostInspector = renderInShadowRoot(<PostDecryptUI />, node.afterShadow)
return {
onNodeMutation: collectPostInfo,
onTargetChanged: collectPostInfo,
onRemove() {
unmount()
unmountPostInspector()
unmountCommentBox()
commentWatcher.stopWatch()
commentBoxWatcher.stopWatch()
},
}
})
.setDomProxyOption({ afterShadowRootInit: { mode: 'closed' } })
.omitWarningForRepeatedKeys()
.startWatch()

function zipPostLinkPreview(node: DomProxy) {
Expand Down
7 changes: 4 additions & 3 deletions src/utils/hooks/useCapturedEvents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@ import { useEffect, useCallback } from 'react'
* ! Call this hook inside Shadow Root!
*/
export function useCapturedInput(
ref: React.MutableRefObject<HTMLInputElement | undefined>,
ref: React.MutableRefObject<HTMLInputElement | undefined | null>,
onChange: (newVal: string) => void,
) {
const stop = useCallback((e: Event) => e.stopPropagation(), [])
const use = useCallback((e: Event) => onChange((e.currentTarget as HTMLInputElement).value), [onChange])
function binder(keys: (keyof HTMLElementEventMap)[], fn: (e: Event) => void) {
function binder<T extends keyof HTMLElementEventMap>(keys: T[], fn: (e: HTMLElementEventMap[T]) => void) {
return () => {
if (!ref.current) return
keys.forEach(k => ref.current!.addEventListener(k, fn, true))
Expand All @@ -19,7 +19,7 @@ export function useCapturedInput(
}
}
}
useEffect(binder(['keydown', 'keyup', 'keypress', 'change'], use), [ref.current])
useEffect(binder(['change'], use), [ref.current])
useEffect(
binder(
[
Expand All @@ -41,4 +41,5 @@ export function useCapturedInput(
),
[ref.current],
)
return binder
}
1 change: 1 addition & 0 deletions src/utils/jss/renderInShadowRoot.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ const jss = create({ ...jssPreset(), Renderer: ConstructableStyleSheetsRenderer
* @param shadow ShadowRoot that want to inject to
*/
export function renderInShadowRoot(node: React.ReactNode, shadow: ShadowRoot) {
if (shadow.mode === 'open') console.warn('Do not render with open ShadowRoot!')
ReactDOM.render(<RenderInShadowRootWrapper children={node} />, shadow as any)
livingShadowRoots.add(shadow)
applyAdoptedStyleSheets()
Expand Down
1 change: 1 addition & 0 deletions src/utils/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export function getUrl(path: string, fallback: string = '') {
export function dispatchCustomEvents<T extends keyof CustomEvents>(event: T, ...x: CustomEvents[T]) {
document.dispatchEvent(new CustomEvent(CustomEventId, { detail: [event, x] }))
}
Object.assign(window, { dispatchCustomEvents })
/**
* Select all text in a node
* @param el Element
Expand Down

0 comments on commit 339f420

Please sign in to comment.