Skip to content

Commit

Permalink
Drop old database maskbook-avatar-store
Browse files Browse the repository at this point in the history
Signed-off-by: Jack Works <[email protected]>
  • Loading branch information
Jack-Works committed Jun 1, 2019
1 parent 8b2d118 commit 85ec537
Show file tree
Hide file tree
Showing 38 changed files with 528 additions and 325 deletions.
11 changes: 8 additions & 3 deletions src/components/Dashboard/Dashboard.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import * as React from 'react'
import Identity from './Identity'
import { Person } from '../../extension/background-script/PeopleService'
import { Typography, Button, Theme } from '@material-ui/core'
import { styled } from '@material-ui/styles'
import { Person } from '../../database'
import { PersonIdentifier } from '../../database/type'

interface Props {
identities: Person[]
addAccount(): void
exportBackup(): void
onProfileClick(username: string): void
onProfileClick(identifier: PersonIdentifier): void
}

const Main = styled('div')(({ theme }: { theme: Theme }) => ({
Expand All @@ -32,7 +33,11 @@ export default function Dashboard(props: Props) {
<Typography variant="h5">Maskbook Identity Management</Typography>
<main>
{props.identities.map(x => (
<Identity key={x.username} person={x} onClick={() => props.onProfileClick(x.username)} />
<Identity
key={x.identifier.toString()}
person={x}
onClick={() => props.onProfileClick(x.identifier)}
/>
))}
</main>
<div>
Expand Down
6 changes: 3 additions & 3 deletions src/components/Dashboard/Identity.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import * as React from 'react'
import { FixedWidthFonts } from '../../utils/theme'
import classNames from 'classnames'
import { Avatar } from '../../utils/components/Avatar'
import { Person } from '../../extension/background-script/PeopleService'
import { makeStyles, Typography, Card, CardHeader } from '@material-ui/core'
import { styled } from '@material-ui/styles'
import { Person } from '../../database'

interface Props {
person: Person
Expand Down Expand Up @@ -36,7 +36,7 @@ const useStyles = makeStyles(theme => ({
},
}))
export default function Identity({ person, onClick }: Props) {
const { avatar, fingerprint, nickname, username } = person
const { avatar, fingerprint, nickname, identifier } = person
const classes = useStyles()
return (
<Card onClick={onClick} className={classes.card}>
Expand All @@ -48,7 +48,7 @@ export default function Identity({ person, onClick }: Props) {
<Typography display="inline" className={classes.text}>
{nickname}
</Typography>
<Typography display="inline">{username}</Typography>
<Typography display="inline">{identifier.userId}</Typography>
</>
}
subheader={<FixedWidth>{fingerprint}</FixedWidth>}
Expand Down
8 changes: 4 additions & 4 deletions src/components/DataSource/PeopleRef.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
import React from 'react'
import { Person } from '../../extension/background-script/PeopleService'
import { ValueRef } from '@holoflows/kit/es'
import { MessageCenter } from '../../utils/messages'
import Services from '../../extension/service'
import { Person } from '../../database'

const ref = new ValueRef<Person[]>([])
Services.People.queryPeople().then(p => (ref.value = p))
Services.People.queryPeople('facebook.com').then(p => (ref.value = p))
MessageCenter.on('newPerson', p => {
const old = ref.value.filter(x => x.username !== p.username)
const old = ref.value.filter(x => x.identifier.toString() !== p.identifier.toString())
ref.value = [...old, p]
})
export function usePeople() {
const [people, setPeople] = React.useState<Person[]>(ref.value)
React.useEffect(() => ref.addListener(val => setPeople(val.filter(x => x.username !== '$self'))), [])
React.useEffect(() => ref.addListener(val => setPeople(val)), [])
return people
}
27 changes: 7 additions & 20 deletions src/components/InjectedComponents/AdditionalPostBox.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import * as React from 'react'
import { useAsync } from '../../utils/components/AsyncComponent'
import { Person } from '../../extension/background-script/PeopleService'
import { usePeople } from '../DataSource/PeopleRef'
import { SelectPeopleUI } from './SelectPeople'
import { myUsername, getUsername } from '../../extension/content-script/injections/LiveSelectors'
Expand All @@ -12,6 +11,8 @@ import { pasteIntoPostBox } from '../../extension/content-script/tasks'
import { geti18nString } from '../../utils/i18n'
import { makeStyles } from '@material-ui/styles'
import { Card, CardHeader, Typography, Divider, Paper, InputBase, Button, Box } from '@material-ui/core'
import { PersonIdentifier } from '../../database/type'
import { Person } from '../../database'

interface Props {
people: Person[]
Expand Down Expand Up @@ -65,11 +66,7 @@ export function AdditionalPostBoxUI(props: Props) {
</Paper>
<Divider />
<Paper elevation={2}>
<SelectPeopleUI
people={people.filter(x => x.username !== (myself ? myself.username : ''))}
onSetSelected={selectPeople}
selected={selectedPeople}
/>
<SelectPeopleUI people={people} onSetSelected={selectPeople} selected={selectedPeople} />
</Paper>
<Divider />
<Box display="flex" className={classes.grayArea}>
Expand All @@ -87,14 +84,10 @@ export function AdditionalPostBoxUI(props: Props) {
}

export function AdditionalPostBox() {
const [avatar, setAvatar] = React.useState<string | undefined>('')
let nickname
{
const link = myUsername.evaluateOnce()[0]
if (link) nickname = link.innerText
}
const username = getUsername()
useAsync(() => Services.People.queryAvatar(username || ''), []).then(setAvatar)
const [identity, setIdentity] = React.useState<Person | undefined>(undefined)
// ! TODO: Query my identity //
// useAsync(() => ).then(setIdentity)
const onRequestPost = React.useCallback(async (people, text) => {
const [encrypted, token] = await Services.Crypto.encryptTo(text, people)
const fullPost = geti18nString('additional_post_box__encrypted_post_pre', encrypted)
Expand All @@ -104,11 +97,5 @@ export function AdditionalPostBox() {
if (!username) {
return <AdditionalPostBoxUI people={usePeople()} onRequestPost={onRequestPost} />
}
return (
<AdditionalPostBoxUI
people={usePeople()}
myself={{ avatar, nickname, username }}
onRequestPost={onRequestPost}
/>
)
return <AdditionalPostBoxUI people={usePeople()} myself={identity} onRequestPost={onRequestPost} />
}
2 changes: 1 addition & 1 deletion src/components/InjectedComponents/DecryptedPost.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ import React, { useCallback } from 'react'
import AsyncComponent from '../../utils/components/AsyncComponent'
import { AdditionalContent } from './AdditionalPostContent'
import { useShareMenu } from './SelectPeopleDialog'
import { Person } from '../../extension/background-script/PeopleService'
import { sleep } from '../../utils/utils'
import Services from '../../extension/service'
import { geti18nString } from '../../utils/i18n'
import { makeStyles } from '@material-ui/styles'
import { Link, Box } from '@material-ui/core'
import { Person } from '../../database'

interface DecryptPostSuccessProps {
data: { signatureVerifyResult: boolean; content: string }
Expand Down
23 changes: 13 additions & 10 deletions src/components/InjectedComponents/SelectPeople.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import * as React from 'react'
import { Person } from '../../extension/background-script/PeopleService'
import { Avatar } from '../../utils/components/Avatar'
import { geti18nString } from '../../utils/i18n'
import {
Expand All @@ -13,6 +12,7 @@ import {
List,
Box,
} from '@material-ui/core'
import { Person } from '../../database'

interface PeopleInListProps {
person: Person
Expand All @@ -29,7 +29,7 @@ function PersonInList({ person, onClick, disabled }: PeopleInListProps) {
<Avatar person={person} />
</ListItemAvatar>
<ListItemText
primary={person.nickname || person.username}
primary={person.nickname || person.identifier.userId}
secondary={person.fingerprint ? person.fingerprint.toLowerCase() : undefined}
/>
</ListItem>
Expand All @@ -47,7 +47,7 @@ function PersonInChip({ disabled, onDelete, person }: PersonInChipProps) {
style={{ marginRight: 6, marginBottom: 6 }}
color="primary"
onDelete={disabled ? undefined : onDelete}
label={person.nickname || person.username}
label={person.nickname || person.identifier.userId}
avatar={avatar}
/>
)
Expand All @@ -74,27 +74,30 @@ export function SelectPeopleUI({ people, frozenSelected, onSetSelected, selected
const classes = useStyles()
const [search, setSearch] = React.useState('')
const listBeforeSearch = people.filter(x => {
if (selected.find(y => y.username === x.username)) return false
if (selected.find(y => y.identifier.userId === x.identifier.userId)) return false
return true
})
const listAfterSearch = listBeforeSearch.filter(x => {
if (frozenSelected && frozenSelected.find(y => x.username === y.username)) return false
if (frozenSelected && frozenSelected.find(y => x.identifier.userId === y.identifier.userId)) return false
if (search === '') return true
return (
!!x.username.toLocaleLowerCase().match(search.toLocaleLowerCase()) ||
!!x.identifier.userId.toLocaleLowerCase().match(search.toLocaleLowerCase()) ||
!!(x.fingerprint || '').toLocaleLowerCase().match(search.toLocaleLowerCase())
)
})
return (
<>
<Box display="flex" className={classes.selectedArea}>
{frozenSelected && frozenSelected.map(p => <PersonInChip disabled key={p.username} person={p} />)}
{frozenSelected &&
frozenSelected.map(p => <PersonInChip disabled key={p.identifier.userId} person={p} />)}
{selected.map(p => (
<PersonInChip
disabled={disabled}
key={p.username}
key={p.identifier.userId}
person={p}
onDelete={() => onSetSelected(selected.filter(x => x.username !== p.username))}
onDelete={() =>
onSetSelected(selected.filter(x => x.identifier.userId !== p.identifier.userId))
}
/>
))}
<InputBase
Expand Down Expand Up @@ -142,7 +145,7 @@ export function SelectPeopleUI({ people, frozenSelected, onSetSelected, selected
)}
{listAfterSearch.map(p => (
<PersonInList
key={p.username}
key={p.identifier.userId}
person={p}
disabled={disabled}
onClick={() => {
Expand Down
2 changes: 1 addition & 1 deletion src/components/InjectedComponents/SelectPeopleDialog.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import React, { useState, useCallback } from 'react'
import { SelectPeopleUI } from './SelectPeople'
import { Person } from '../../extension/background-script/PeopleService'
import { geti18nString } from '../../utils/i18n'
import { makeStyles } from '@material-ui/styles'
import { Dialog, DialogTitle, DialogContent, DialogActions, Button, CircularProgress } from '@material-ui/core'
import { Person } from '../../database'
interface Props {
open: boolean
people: Person[]
Expand Down
9 changes: 7 additions & 2 deletions src/crypto/crypto-alpha-40.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import { encodeText, encodeArrayBuffer, decodeArrayBuffer, decodeText } from '../utils/type-transform/EncodeDecode'
import { toECDH, addUint8Array, toECDSA } from '../utils/type-transform/CryptoUtils'
import {
encodeText,
encodeArrayBuffer,
decodeArrayBuffer,
decodeText,
} from '../utils/type-transform/String-ArrayBuffer'
import { toECDH, addUint8Array, toECDSA } from '../utils/type-transform/ECDSA-ECDH'
// tslint:disable: no-parameter-reassignment
export type PublishedAESKey = { encryptedKey: string; salt: string }
export type PublishedAESKeyRecord = {
Expand Down
32 changes: 26 additions & 6 deletions src/database/avatar.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/// <reference path="./idb.d.ts" />
/// <reference path="./global.d.ts" />
import { openDB, DBSchema } from 'idb/with-async-ittr'
import { Identifier, PersonIdentifier, GroupIdentifier } from './type'

Expand All @@ -24,7 +24,7 @@ interface AvatarDB extends DBSchema {
}
//#endregion

const db = openDB<AvatarDB>('maskbook-avatar-cache-v2', 1, {
const db = openDB<AvatarDB>('maskbook-avatar-cache', 1, {
upgrade(db, oldVersion, newVersion, transaction) {
// Out line keys
const avatarStore = db.createObjectStore('avatars')
Expand Down Expand Up @@ -62,20 +62,22 @@ export async function queryAvatarDB(id: IdentityWithAvatar): Promise<ArrayBuffer
/**
* Store avatar metadata
*/
async function updateAvatarMetaDB(id: IdentityWithAvatar, newMeta: Partial<AvatarMetadataRecord>) {
export async function updateAvatarMetaDB(id: IdentityWithAvatar, newMeta: Partial<AvatarMetadataRecord>) {
const t = (await db).transaction('metadata', 'readwrite')
const meta = await t.objectStore('metadata').get(id.toString())
const newRecord = Object.assign({}, meta, newMeta)
await t.objectStore('metadata').put(newRecord)
return newRecord
}
/**
* Find avatar lastUpdateTime or lastAccessTime out-of-date
* @param deadline - Select all identifiers before a date, defaults to 30 days
* @param attribute - Which attribute want to query
* @param deadline - Select all identifiers before a date
* defaults to 30 days for lastAccessTime
* defaults to 7 days for lastUpdateTime
*/
export async function queryAvatarOutdatedDB(
attribute: 'lastUpdateTime' | 'lastAccessTime',
deadline: Date = new Date(Date.now() - 1000 * 60 * 60 * 24 * 30),
deadline: Date = new Date(Date.now() - 1000 * 60 * 60 * 24 * (attribute === 'lastAccessTime' ? 30 : 7)),
) {
const t = (await db).transaction('metadata')
const outdated: IdentityWithAvatar[] = []
Expand All @@ -85,6 +87,24 @@ export async function queryAvatarOutdatedDB(
}
return outdated
}
/**
* Query if the avatar is outdated
* @param attribute - Which attribute want to query
* @param deadline - Select all identifiers before a date
* defaults to 30 days for lastAccessTime
* defaults to 7 days for lastUpdateTime
*/
export async function isAvatarOutdatedDB(
identifier: PersonIdentifier | GroupIdentifier,
attribute: 'lastUpdateTime' | 'lastAccessTime',
deadline: Date = new Date(Date.now() - 1000 * 60 * 60 * 24 * (attribute === 'lastAccessTime' ? 30 : 7)),
): Promise<boolean> {
const t = (await db).transaction('metadata')
const meta = await t.objectStore('metadata').get(identifier.toString())
if (!meta) return true
if (deadline > meta[attribute]) return true
return false
}
/**
* Batch delete avatars
*/
Expand Down
9 changes: 9 additions & 0 deletions src/database/global.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
declare module 'idb/with-async-ittr' {
export * from 'idb'
}
/**
* IDBFactory v3
*/
interface IDBFactory {
databases?(): Promise<Array<{ name: string; version: number }>>
}
47 changes: 47 additions & 0 deletions src/database/helpers/avatar.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { PersonIdentifier, GroupIdentifier } from '../type'
import { queryAvatarDB, isAvatarOutdatedDB, storeAvatarDB } from '../avatar'

const avatarCache = new Map<string, string>()

/**
* Get a (cached) blob url for an identifier.
*/
export async function getAvatarBlobURL(identifier: PersonIdentifier | GroupIdentifier): Promise<string | undefined> {
const id = identifier.toString()
if (avatarCache.has(id)) return avatarCache.get(id)!
const buffer = await queryAvatarDB(identifier)
if (!buffer) return undefined
const blob = new Blob([buffer], { type: 'image/png' })
const url = URL.createObjectURL(blob)
avatarCache.set(id, url)
return url
}

/**
* Store an avatar with a url for an identifier.
* @param avatar - Avatar to store. If it is a string, will try to fetch it.
* @param identifier - This avatar belongs to.
*/

export async function storeAvatar(identifier: PersonIdentifier | GroupIdentifier, avatar: ArrayBuffer | string) {
try {
if (typeof avatar === 'string') {
if (await isAvatarOutdatedDB(identifier, 'lastUpdateTime')) {
await storeAvatarDB(identifier, await downloadAvatar(avatar))
}
// else do nothing
} else {
await storeAvatarDB(identifier, avatar)
}
} catch (e) {
console.error('Store avatar failed', e)
}
}
/**
* Download avatar from url
*/
async function downloadAvatar(url: string): Promise<ArrayBuffer> {
const res = await fetch(url)
if (!res.ok) throw new Error('Fetch avatar failed.')
return res.arrayBuffer()
}
Loading

0 comments on commit 85ec537

Please sign in to comment.