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

twitter support, stage 3 #193

Merged
merged 16 commits into from
Oct 9, 2019
Merged
Show file tree
Hide file tree
Changes from 13 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
5 changes: 2 additions & 3 deletions src/components/DebugModeUI/PostHashDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,7 @@ import { Person } from '../../database'
import { useAsync } from '../../utils/components/AsyncComponent'
import Services from '../../extension/service'
import { PostIVIdentifier } from '../../database/type'
import { deconstructPayload, Payload } from '../../utils/type-transform/Payload'
import { DialogContentText, DialogContent } from '@material-ui/core'
import { deconstructPayload } from '../../utils/type-transform/Payload'

const useStyles = makeStyles({
avatar: {
Expand Down Expand Up @@ -71,7 +70,7 @@ function SimpleDialog(props: SimpleDialogProps) {

export function DebugModeUI_PostHashDialog(props: { post: string; network: string }) {
const [open, setOpen] = React.useState(false)
const payload = deconstructPayload(props.post)
const payload = deconstructPayload(props.post, null)
const [hashMap, setHashMap] = useState<[string, string][]>([])
const friends = useFriendsList()
useAsync(() => {
Expand Down
13 changes: 10 additions & 3 deletions src/components/InjectedComponents/AdditionalPostBox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,19 @@ import Services from '../../extension/service'
import { geti18nString } from '../../utils/i18n'
import { makeStyles } from '@material-ui/styles'
import { Box, Button, Card, CardHeader, Divider, InputBase, Paper, Typography } from '@material-ui/core'
import { Person, Group } from '../../database'
import { Group, Person } from '../../database'
import { NotSetupYetPrompt } from '../shared/NotSetupYetPrompt'
import { useCurrentIdentity, useFriendsList, useMyIdentities, useGroupsList } from '../DataSource/useActivatedUI'
import { useCurrentIdentity, useFriendsList, useGroupsList, useMyIdentities } from '../DataSource/useActivatedUI'
Misaka-0x447f marked this conversation as resolved.
Show resolved Hide resolved
import { getActivatedUI } from '../../social-network/ui'
import { ChooseIdentity } from '../shared/ChooseIdentity'
import { useAsync } from '../../utils/components/AsyncComponent'

interface Props {
availableTarget: (Person | Group)[]

onRequestPost(people: (Person | Group)[], text: string): void
}

const useStyles = makeStyles({
root: { margin: '10px 0' },
header: { padding: '8px 12px 0' },
Expand All @@ -35,6 +38,7 @@ const useStyles = makeStyles({
grayArea: { background: '#f5f6f7', padding: 8, wordBreak: 'break-all' },
button: { padding: '2px 30px', flex: 1 },
})

export function AdditionalPostBoxUI(props: Props) {
const { availableTarget } = props
const classes = useStyles()
Expand Down Expand Up @@ -113,7 +117,10 @@ export function AdditionalPostBox(props: Partial<Props>) {
[identity],
)

if (identity.length === 0) {
const [showWelcome, setShowWelcome] = useState(false)
useAsync(getActivatedUI().shouldDisplayWelcome, []).then(x => setShowWelcome(x))

if (showWelcome) {
Misaka-0x447f marked this conversation as resolved.
Show resolved Hide resolved
return <NotSetupYetPrompt />
}

Expand Down
6 changes: 3 additions & 3 deletions src/components/InjectedComponents/DecryptedPost.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import React, { useCallback, useState, useMemo } from 'react'
import React, { useMemo, useState } from 'react'
import AsyncComponent from '../../utils/components/AsyncComponent'
import { AdditionalContent } from './AdditionalPostContent'
import { useShareMenu } from './SelectPeopleDialog'
import { sleep } from '../../utils/utils'
import { ServicesWithProgress } from '../../extension/service'
import { geti18nString } from '../../utils/i18n'
import { makeStyles } from '@material-ui/styles'
import { Box, Button, Link, SnackbarContent, useMediaQuery, useTheme } from '@material-ui/core'
import { Box, Link, SnackbarContent, useMediaQuery, useTheme } from '@material-ui/core'
import { Person } from '../../database'
import { Identifier, PersonIdentifier } from '../../database/type'
import { NotSetupYetPrompt } from '../shared/NotSetupYetPrompt'
Expand Down Expand Up @@ -130,7 +130,7 @@ function DecryptPost(props: DecryptPostProps) {
}, [requestAppendRecipients, postBy, whoAmI])
const debugHashJSX = useMemo(() => {
if (!isDebugging) return null
const postPayload = deconstructPayload(encryptedText)
const postPayload = deconstructPayload(encryptedText, null)
if (!postPayload) return null
const postByMyself = <DebugModeUI_PostHashDialog network={postBy.network} post={encryptedText} />
return (
Expand Down
2 changes: 1 addition & 1 deletion src/components/InjectedComponents/PostInspector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export function PostInspector(props: PostInspectorProps) {
const decodeResult = getActivatedUI().publicKeyDecoder(post)
const isDebugging = useValueRef(debugModeSetting)
const type = {
encryptedPost: deconstructPayload(post),
encryptedPost: deconstructPayload(post, getActivatedUI().payloadDecoder),
provePost: decodeResult ? [decodeResult] : null,
}
if (type.provePost) Services.People.writePersonOnGun(postBy, { provePostId: postId })
Expand Down
7 changes: 1 addition & 6 deletions src/components/Welcomes/Banner.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -84,13 +84,8 @@ export function Banner({
getActivatedUI().ignoreSetupAccount(env, {})
unmount()
}, [unmount])
if (typeof props.networkIdentifier === 'function' && props.getStarted === undefined) {
throw new TypeError(
'You cannot use getStartedDefault when networkIdentifier is a function. Please implement this function yourself.',
)
}
const getStartedDefault = useCallback(() => {
setStorage(props.networkIdentifier as string, { forceDisplayWelcome: false })
setStorage(props.networkIdentifier, { forceDisplayWelcome: false })
unmount()
Services.Welcome.openWelcomePage(lastRecognizedIdentity)
}, [lastRecognizedIdentity, props.networkIdentifier, unmount])
Expand Down
9 changes: 7 additions & 2 deletions src/components/shared/NotSetupYetPrompt.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import React from 'react'
import Services from '../../extension/service'
import { geti18nString } from '../../utils/i18n'
import { SnackbarContent, Button, makeStyles } from '@material-ui/core'
import { Button, makeStyles, SnackbarContent } from '@material-ui/core'
import { GetContext } from '@holoflows/kit/es'
import { useLastRecognizedIdentity } from '../DataSource/useActivatedUI'
import { getActivatedUI } from '../../social-network/ui'
import { setStorage } from '../../utils/browser.storage'

const useNotSetUpYetStyles = makeStyles({
root: {
Expand Down Expand Up @@ -53,10 +55,13 @@ export function NotSetupYetPrompt() {
return (
<NotSetupYetPromptUI
onSetupClick={() => {
if (isContent) {
setStorage(getActivatedUI().networkIdentifier, { forceDisplayWelcome: false }).then()
}
if (GetContext() === 'options') {
location.hash = '/welcome'
} else if (id) {
Services.Welcome.openWelcomePage(id)
Services.Welcome.openWelcomePage(id).then()
}
}}
preparingSetup={isContent && isCurrentIdentityUnknown()}
Expand Down
7 changes: 5 additions & 2 deletions src/extension/background-script/CryptoServices/decryptFrom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import { PersonIdentifier, PostIVIdentifier } from '../../../database/type'
import { queryPostDB, updatePostDB } from '../../../database/post'
import { addPerson } from './addPerson'
import { MessageCenter } from '../../../utils/messages'
import { getNetworkWorker } from '../../../social-network/worker'

type Progress = {
progress: 'finding_person_public_key' | 'finding_post_key'
}
Expand Down Expand Up @@ -44,11 +46,12 @@ export async function* decryptFromMessageWithProgress(
by: PersonIdentifier,
whoAmI: PersonIdentifier,
): ReturnOfDecryptFromMessageWithProgress {
const decoder = getNetworkWorker(by.network).payloadDecoder
// If any of parameters is changed, we will not handle it.
const data = deconstructPayload(encrypted)!
const data = deconstructPayload(encrypted, decoder)!
if (!data) {
try {
deconstructPayload(encrypted, true)
deconstructPayload(encrypted, decoder, { throws: true })
} catch (e) {
return { error: e.message }
}
Expand Down
24 changes: 15 additions & 9 deletions src/extension/background-script/CryptoServices/encryptTo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,20 @@ import { queryLocalKeyDB } from '../../../database/people'
import { PersonIdentifier } from '../../../database/type'
import { prepareOthersKeyForEncryptionV39 } from '../prepareOthersKeyForEncryption'
import { geti18nString } from '../../../utils/i18n'
import { getNetworkWorker } from '../../../social-network/worker'

type EncryptedText = string
type OthersAESKeyEncryptedToken = string
/**
* This map stores <iv, othersAESKeyEncrypted>.
*/
const OthersAESKeyEncryptedMap = new Map<OthersAESKeyEncryptedToken, (Alpha39.PublishedAESKeyRecordV39)[]>()

/**
* Encrypt to a user
* @param content Original text
* @param to Encrypt target
* @param content Original text
* @param to Encrypt target
* @param whoAmI Encrypt source
* @returns Will return a tuple of [encrypted: string, token: string] where
* - `encrypted` is the encrypted string
* - `token` is used to call `publishPostAESKey` before post the content
Expand Down Expand Up @@ -55,13 +58,16 @@ export async function encryptTo(
const key = encodeArrayBuffer(iv)
OthersAESKeyEncryptedMap.set(key, othersAESKeyEncrypted)
return [
`${constructAlpha39({
encryptedText: encryptedTextStr,
iv: ivStr,
ownersAESKeyEncrypted: ownersAESKeyStr,
signature: signature,
version: -39,
})}`,
`${constructAlpha39(
{
encryptedText: encryptedTextStr,
iv: ivStr,
ownersAESKeyEncrypted: ownersAESKeyStr,
signature: signature,
version: -39,
},
getNetworkWorker(whoAmI.network).payloadEncoder,
)}`,
key,
]
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@ import { encodeArrayBuffer } from '../../../utils/type-transform/String-ArrayBuf
import { toCompressSecp256k1Point } from '../../../utils/type-transform/SECP256k1-Compression'
import { queryMyIdentityAtDB } from '../../../database/people'
import { PersonIdentifier } from '../../../database/type'
import { getWorker } from '../../../social-network/worker'
import { getNetworkWorker } from '../../../social-network/worker'
//#endregion
//#region ProvePost, create & verify
export async function getMyProveBio(whoAmI: PersonIdentifier): Promise<string | null> {
const myIdentity = await queryMyIdentityAtDB(whoAmI)
if (!myIdentity) return null
const pub = await crypto.subtle.exportKey('jwk', myIdentity.publicKey)
const compressed = toCompressSecp256k1Point(pub.x!, pub.y!)
return getWorker(whoAmI.network).publicKeyEncoder(encodeArrayBuffer(compressed))
return getNetworkWorker(whoAmI.network).publicKeyEncoder(encodeArrayBuffer(compressed))
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ import { geti18nString } from '../../../utils/i18n'
import { unCompressSecp256k1Point } from '../../../utils/type-transform/SECP256k1-Compression'
import { storeNewPersonDB } from '../../../database/people'
import { PersonIdentifier } from '../../../database/type'
import { getWorker } from '../../../social-network/worker'
import { getNetworkWorker } from '../../../social-network/worker'
import { import_ECDH_256k1_Key } from '../../../utils/crypto.subtle'

export async function verifyOthersProve(bio: string, others: PersonIdentifier): Promise<boolean> {
const compressedX = getWorker(others.network).publicKeyDecoder(bio)
const compressedX = getNetworkWorker(others.network).publicKeyDecoder(bio)
if (!compressedX) return false
const { x, y } = unCompressSecp256k1Point(decodeArrayBuffer(compressedX))
const key: JsonWebKey = {
Expand Down
7 changes: 4 additions & 3 deletions src/extension/options-page/Developer.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import React from 'react'
import { Button, TextField } from '@material-ui/core'
import { Identifier, PersonIdentifier } from '../../database/type'
import {
ListSubheader,
Grid,
Expand All @@ -8,11 +11,9 @@ import {
ListItemSecondaryAction,
Switch,
} from '@material-ui/core'
import { PersonIdentifier, Identifier } from '../../database/type'
import { deconstructPayload } from '../../utils/type-transform/Payload'
import Services from '../service'
import { Person } from '../../database'
import React from 'react'
import { AddProve } from './DeveloperComponents/AddProve'
import { DecryptPostDeveloperMode } from './DeveloperComponents/DecryptPost'
import { SeeMyProvePost } from './DeveloperComponents/SeeMyProvePost'
Expand All @@ -37,7 +38,7 @@ async function assimilateGoo(content: string, me: Person | null): Promise<string
// TODO: actually use the UI thing because we want to be able to *drumroll* add receipients
try {
const [, by] = content.split(':||')
const pl = deconstructPayload(content, true)!
const pl = deconstructPayload(content, null, { throws: true })!
const r = await Services.Crypto.decryptFrom(
pl.encryptedText,
Identifier.fromString('person:' + by) as PersonIdentifier,
Expand Down
4 changes: 2 additions & 2 deletions src/social-network-provider/facebook.com/UI/collectPosts.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,9 @@ export function collectPostsFacebook(this: SocialNetworkUI) {
info.postID.value = getPostID(metadata)
}
collectPostInfo()
info.postPayload.value = deconstructPayload(info.postContent.value)
info.postPayload.value = deconstructPayload(info.postContent.value, this.payloadDecoder)
info.postContent.addListener(newVal => {
info.postPayload.value = deconstructPayload(newVal)
info.postPayload.value = deconstructPayload(newVal, this.payloadDecoder)
})
return {
onNodeMutation: collectPostInfo,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,14 @@ import { isMobileFacebook } from '../isMobile'
import { PostInfo } from '../../../social-network/ui'
import { injectPostInspectorDefault } from '../../../social-network/defaults/injectPostInspector'

export const defaultBehavior = injectPostInspectorDefault({
zipPost(node) {
zipEncryptedPostContent(node)
zipPostLinkPreview(node)
},
})
export function injectPostInspectorFacebook(current: PostInfo, node: DOMProxy) {
clickSeeMore(node)
return defaultBehavior(current, node)
return injectPostInspectorDefault({
zipPost(node) {
zipEncryptedPostContent(node)
zipPostLinkPreview(node)
},
})(current, node)
}
function zipPostLinkPreview(node: DOMProxy) {
const parentEle = node.current.parentElement!
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ import { LiveSelector, MutationObserverWatcher } from '@holoflows/kit/es'
import { isMobileFacebook } from '../isMobile'
import { renderInShadowRoot } from '../../../utils/jss/renderInShadowRoot'
import { Banner } from '../../../components/Welcomes/Banner'
import { SocialNetworkUI } from '../../../social-network/ui'
import { facebookUISelf } from '../ui-provider'

export function injectWelcomeBannerFacebook(this: SocialNetworkUI) {
export function injectWelcomeBannerFacebook() {
const to = new MutationObserverWatcher(
new LiveSelector()
.querySelector<HTMLDivElement>(isMobileFacebook ? '#MComposer' : '#pagelet_composer')
Expand All @@ -15,7 +15,7 @@ export function injectWelcomeBannerFacebook(this: SocialNetworkUI) {
.startWatch()

const unmount = renderInShadowRoot(
<Banner networkIdentifier={this.networkIdentifier} unmount={() => unmount()} />,
<Banner networkIdentifier={facebookUISelf.networkIdentifier} unmount={() => unmount()} />,
to.firstDOMProxy.beforeShadow,
)
}
4 changes: 2 additions & 2 deletions src/social-network-provider/facebook.com/shared-provider.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { SocialNetworkWorkerAndUI } from '../../social-network/shared'
import { SocialNetworkWorkerAndUIDefinition } from '../../social-network/shared'
import { regularUsername } from './parse-username'

export const sharedProvider: SocialNetworkWorkerAndUI = {
export const sharedProvider: SocialNetworkWorkerAndUIDefinition = {
version: 1,
internalName: 'facebook',
isDangerousNetwork: false,
Expand Down
6 changes: 3 additions & 3 deletions src/social-network-provider/facebook.com/ui-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,12 @@ import { isMobileFacebook } from './isMobile'
import { geti18nString } from '../../utils/i18n'
import { injectCommentBoxDefaultFactory } from '../../social-network/defaults/injectCommentBox'

const self = defineSocialNetworkUI({
export const facebookUISelf = defineSocialNetworkUI({
...sharedProvider,
init(env, pref) {
sharedProvider.init(env, pref)
InitFriendsValueRef(self, 'facebook.com')
InitMyIdentitiesValueRef(self, 'facebook.com')
InitFriendsValueRef(facebookUISelf, 'facebook.com')
InitMyIdentitiesValueRef(facebookUISelf, 'facebook.com')
},
shouldActivate() {
return location.hostname.endsWith('facebook.com')
Expand Down
17 changes: 14 additions & 3 deletions src/social-network-provider/twitter.com/index.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { SocialNetworkWorkerAndUI } from '../../social-network/shared'
import { SocialNetworkWorkerAndUIDefinition } from '../../social-network/shared'
import { usernameValidator } from './utils/user'
import { regexMatch } from '../../utils/utils'
import { batchReplace, regexMatch } from '../../utils/utils'
import { isNil } from 'lodash-es'

export const host = 'twitter.com'
export const hostURL = 'https://twitter.com'
export const hostMobileURL = 'https://mobile.twitter.com'

export const sharedSettings: SocialNetworkWorkerAndUI = {
export const sharedSettings: SocialNetworkWorkerAndUIDefinition = {
version: 1,
internalName: 'twitter',
isDangerousNetwork: false,
Expand All @@ -16,5 +17,15 @@ export const sharedSettings: SocialNetworkWorkerAndUI = {
init() {},
publicKeyEncoder: (text: string) => `🎭${text}🎭`,
publicKeyDecoder: (text: string) => regexMatch(text, /(🎭)(.+)(🎭)/, 2),
payloadEncoder: (text: string) =>
`https://google.com/${batchReplace(text, [['🎼', '%20'], [':||', '%40'], ['+', '-'], ['=', '_'], ['|', '.']])}`,
Misaka-0x447f marked this conversation as resolved.
Show resolved Hide resolved
payloadDecoder: (text: string) => {
let r = regexMatch(text, /https:\/\/google\.com\/%20(.+)%40/, 1)
if (isNil(r)) {
return 'null'
}
r = batchReplace(r, [['-', '+'], ['_', '='], ['.', '|']])
return `🎼${r}:||`
},
notReadyForProduction: true,
}
4 changes: 2 additions & 2 deletions src/social-network-provider/twitter.com/ui/fetch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,9 @@ const registerPostCollector = (self: SocialNetworkUI) => {
uploadToService(r)
}
collectPostInfo()
info.postPayload.value = deconstructPayload(info.postContent.value)
info.postPayload.value = deconstructPayload(info.postContent.value, self.payloadDecoder)
info.postContent.addListener(newValue => {
info.postPayload.value = deconstructPayload(newValue)
info.postPayload.value = deconstructPayload(newValue, self.payloadDecoder)
})
return {
onNodeMutation: collectPostInfo,
Expand Down
Loading