New release #210
@ -1,7 +1,7 @@
|
|||||||
import { Meta } from '../../types'
|
import { Meta } from '../../types'
|
||||||
import { SigitCardDisplayInfo, SigitStatus, SignStatus } from '../../utils'
|
import { SigitCardDisplayInfo, SigitStatus, SignStatus } from '../../utils'
|
||||||
import { Link } from 'react-router-dom'
|
import { Link } from 'react-router-dom'
|
||||||
import { formatTimestamp, hexToNpub, npubToHex, shorten } from '../../utils'
|
import { formatTimestamp, npubToHex } from '../../utils'
|
||||||
import { appPublicRoutes, appPrivateRoutes } from '../../routes'
|
import { appPublicRoutes, appPrivateRoutes } from '../../routes'
|
||||||
import { Button, Divider, Tooltip } from '@mui/material'
|
import { Button, Divider, Tooltip } from '@mui/material'
|
||||||
import { DisplaySigner } from '../DisplaySigner'
|
import { DisplaySigner } from '../DisplaySigner'
|
||||||
@ -17,9 +17,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
|||||||
import { UserAvatarGroup } from '../UserAvatarGroup'
|
import { UserAvatarGroup } from '../UserAvatarGroup'
|
||||||
|
|
||||||
import styles from './style.module.scss'
|
import styles from './style.module.scss'
|
||||||
import { TooltipChild } from '../TooltipChild'
|
|
||||||
import { getExtensionIconLabel } from '../getExtensionIconLabel'
|
import { getExtensionIconLabel } from '../getExtensionIconLabel'
|
||||||
import { useSigitProfiles } from '../../hooks/useSigitProfiles'
|
|
||||||
import { useSigitMeta } from '../../hooks/useSigitMeta'
|
import { useSigitMeta } from '../../hooks/useSigitMeta'
|
||||||
import { extractFileExtensions } from '../../utils/file'
|
import { extractFileExtensions } from '../../utils/file'
|
||||||
|
|
||||||
@ -33,12 +31,6 @@ export const DisplaySigit = ({ meta, parsedMeta }: SigitProps) => {
|
|||||||
parsedMeta
|
parsedMeta
|
||||||
|
|
||||||
const { signersStatus, fileHashes } = useSigitMeta(meta)
|
const { signersStatus, fileHashes } = useSigitMeta(meta)
|
||||||
|
|
||||||
const profiles = useSigitProfiles([
|
|
||||||
...(submittedBy ? [submittedBy] : []),
|
|
||||||
...signers
|
|
||||||
])
|
|
||||||
|
|
||||||
const { extensions, isSame } = extractFileExtensions(Object.keys(fileHashes))
|
const { extensions, isSame } = extractFileExtensions(Object.keys(fileHashes))
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -54,62 +46,29 @@ export const DisplaySigit = ({ meta, parsedMeta }: SigitProps) => {
|
|||||||
></Link>
|
></Link>
|
||||||
<p className={`line-clamp-2 ${styles.title}`}>{title}</p>
|
<p className={`line-clamp-2 ${styles.title}`}>{title}</p>
|
||||||
<div className={styles.users}>
|
<div className={styles.users}>
|
||||||
{submittedBy &&
|
{submittedBy && (
|
||||||
(function () {
|
|
||||||
const profile = profiles[submittedBy]
|
|
||||||
return (
|
|
||||||
<Tooltip
|
|
||||||
key={submittedBy}
|
|
||||||
title={
|
|
||||||
profile?.display_name ||
|
|
||||||
profile?.name ||
|
|
||||||
shorten(hexToNpub(submittedBy))
|
|
||||||
}
|
|
||||||
placement="top"
|
|
||||||
arrow
|
|
||||||
disableInteractive
|
|
||||||
>
|
|
||||||
<TooltipChild>
|
|
||||||
<DisplaySigner
|
<DisplaySigner
|
||||||
status={isValid ? SignStatus.Signed : SignStatus.Invalid}
|
status={isValid ? SignStatus.Signed : SignStatus.Invalid}
|
||||||
profile={profile}
|
|
||||||
pubkey={submittedBy}
|
pubkey={submittedBy}
|
||||||
/>
|
/>
|
||||||
</TooltipChild>
|
)}
|
||||||
</Tooltip>
|
|
||||||
)
|
|
||||||
})()}
|
|
||||||
{submittedBy && signers.length ? (
|
{submittedBy && signers.length ? (
|
||||||
<Divider orientation="vertical" flexItem />
|
<Divider orientation="vertical" flexItem />
|
||||||
) : null}
|
) : null}
|
||||||
<UserAvatarGroup max={7}>
|
<UserAvatarGroup max={7}>
|
||||||
{signers.map((signer) => {
|
{signers.map((signer) => {
|
||||||
const pubkey = npubToHex(signer)!
|
const pubkey = npubToHex(signer)!
|
||||||
const profile = profiles[pubkey]
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Tooltip
|
|
||||||
key={signer}
|
|
||||||
title={
|
|
||||||
profile?.display_name || profile?.name || shorten(pubkey)
|
|
||||||
}
|
|
||||||
placement="top"
|
|
||||||
arrow
|
|
||||||
disableInteractive
|
|
||||||
>
|
|
||||||
<TooltipChild>
|
|
||||||
<DisplaySigner
|
<DisplaySigner
|
||||||
|
key={pubkey}
|
||||||
status={signersStatus[signer]}
|
status={signersStatus[signer]}
|
||||||
profile={profile}
|
|
||||||
pubkey={pubkey}
|
pubkey={pubkey}
|
||||||
/>
|
/>
|
||||||
</TooltipChild>
|
|
||||||
</Tooltip>
|
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
</UserAvatarGroup>
|
</UserAvatarGroup>
|
||||||
</div>
|
</div>
|
||||||
<div className={`${styles.details} ${styles.date} ${styles.iconLabel}`}>
|
<div className={`${styles.details} ${styles.iconLabel}`}>
|
||||||
<FontAwesomeIcon icon={faCalendar} />
|
<FontAwesomeIcon icon={faCalendar} />
|
||||||
{createdAt ? formatTimestamp(createdAt) : null}
|
{createdAt ? formatTimestamp(createdAt) : null}
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import { Badge } from '@mui/material'
|
import { Badge } from '@mui/material'
|
||||||
import { ProfileMetadata } from '../../types'
|
|
||||||
import styles from './style.module.scss'
|
import styles from './style.module.scss'
|
||||||
import { UserAvatar } from '../UserAvatar'
|
import { UserAvatar } from '../UserAvatar'
|
||||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||||
@ -15,17 +14,11 @@ import { SignStatus } from '../../utils'
|
|||||||
import { Spinner } from '../Spinner'
|
import { Spinner } from '../Spinner'
|
||||||
|
|
||||||
type DisplaySignerProps = {
|
type DisplaySignerProps = {
|
||||||
profile: ProfileMetadata
|
|
||||||
pubkey: string
|
pubkey: string
|
||||||
status: SignStatus
|
status: SignStatus
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DisplaySigner = ({
|
const getStatusIcon = (status: SignStatus) => {
|
||||||
status,
|
|
||||||
profile,
|
|
||||||
pubkey
|
|
||||||
}: DisplaySignerProps) => {
|
|
||||||
const getStatusIcon = (status: SignStatus) => {
|
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case SignStatus.Signed:
|
case SignStatus.Signed:
|
||||||
return <FontAwesomeIcon icon={faCheck} />
|
return <FontAwesomeIcon icon={faCheck} />
|
||||||
@ -45,8 +38,9 @@ export const DisplaySigner = ({
|
|||||||
default:
|
default:
|
||||||
return <FontAwesomeIcon icon={faQuestion} />
|
return <FontAwesomeIcon icon={faQuestion} />
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const DisplaySigner = ({ status, pubkey }: DisplaySignerProps) => {
|
||||||
return (
|
return (
|
||||||
<Badge
|
<Badge
|
||||||
className={styles.signer}
|
className={styles.signer}
|
||||||
@ -56,7 +50,7 @@ export const DisplaySigner = ({
|
|||||||
<div className={styles.statusBadge}>{getStatusIcon(status)}</div>
|
<div className={styles.statusBadge}>{getStatusIcon(status)}</div>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<UserAvatar pubkey={pubkey} image={profile?.picture} />
|
<UserAvatar pubkey={pubkey} />
|
||||||
</Badge>
|
</Badge>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -3,34 +3,56 @@ import { getProfileRoute } from '../../routes'
|
|||||||
import styles from './styles.module.scss'
|
import styles from './styles.module.scss'
|
||||||
import { AvatarIconButton } from '../UserAvatarIconButton'
|
import { AvatarIconButton } from '../UserAvatarIconButton'
|
||||||
import { Link } from 'react-router-dom'
|
import { Link } from 'react-router-dom'
|
||||||
|
import { useProfileMetadata } from '../../hooks/useProfileMetadata'
|
||||||
|
import { Tooltip } from '@mui/material'
|
||||||
|
import { shorten } from '../../utils'
|
||||||
|
import { TooltipChild } from '../TooltipChild'
|
||||||
|
|
||||||
interface UserAvatarProps {
|
interface UserAvatarProps {
|
||||||
name?: string
|
|
||||||
pubkey: string
|
pubkey: string
|
||||||
image?: string
|
isNameVisible?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This component will be used for the displaying username and profile picture.
|
* This component will be used for the displaying username and profile picture.
|
||||||
* Clicking will navigate to the user's profile.
|
* Clicking will navigate to the user's profile.
|
||||||
*/
|
*/
|
||||||
export const UserAvatar = ({ pubkey, name, image }: UserAvatarProps) => {
|
export const UserAvatar = ({
|
||||||
|
pubkey,
|
||||||
|
isNameVisible = false
|
||||||
|
}: UserAvatarProps) => {
|
||||||
|
const profile = useProfileMetadata(pubkey)
|
||||||
|
const name = profile?.display_name || profile?.name || shorten(pubkey)
|
||||||
|
const image = profile?.picture
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Link
|
<Link
|
||||||
to={getProfileRoute(pubkey)}
|
to={getProfileRoute(pubkey)}
|
||||||
className={styles.container}
|
className={styles.container}
|
||||||
tabIndex={-1}
|
tabIndex={-1}
|
||||||
>
|
>
|
||||||
|
<Tooltip
|
||||||
|
key={pubkey}
|
||||||
|
title={name}
|
||||||
|
placement="top"
|
||||||
|
arrow
|
||||||
|
disableInteractive
|
||||||
|
>
|
||||||
|
<TooltipChild>
|
||||||
<AvatarIconButton
|
<AvatarIconButton
|
||||||
src={image}
|
src={image}
|
||||||
hexKey={pubkey}
|
hexKey={pubkey}
|
||||||
aria-label={`account of user ${name || pubkey}`}
|
aria-label={`account of user ${name}`}
|
||||||
color="inherit"
|
color="inherit"
|
||||||
sx={{
|
sx={{
|
||||||
padding: 0
|
padding: 0
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
{name ? <span className={styles.username}>{name}</span> : null}
|
</TooltipChild>
|
||||||
|
</Tooltip>
|
||||||
|
{isNameVisible && name ? (
|
||||||
|
<span className={styles.username}>{name}</span>
|
||||||
|
) : null}
|
||||||
</Link>
|
</Link>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,9 @@
|
|||||||
import { Divider, Tooltip } from '@mui/material'
|
import { Divider, Tooltip } from '@mui/material'
|
||||||
import { useSigitProfiles } from '../../hooks/useSigitProfiles'
|
|
||||||
import {
|
import {
|
||||||
formatTimestamp,
|
formatTimestamp,
|
||||||
fromUnixTimestamp,
|
fromUnixTimestamp,
|
||||||
hexToNpub,
|
hexToNpub,
|
||||||
npubToHex,
|
npubToHex,
|
||||||
shorten,
|
|
||||||
SignStatus
|
SignStatus
|
||||||
} from '../../utils'
|
} from '../../utils'
|
||||||
import { useSigitMeta } from '../../hooks/useSigitMeta'
|
import { useSigitMeta } from '../../hooks/useSigitMeta'
|
||||||
@ -24,10 +22,10 @@ import {
|
|||||||
import { getExtensionIconLabel } from '../getExtensionIconLabel'
|
import { getExtensionIconLabel } from '../getExtensionIconLabel'
|
||||||
import { useSelector } from 'react-redux'
|
import { useSelector } from 'react-redux'
|
||||||
import { State } from '../../store/rootReducer'
|
import { State } from '../../store/rootReducer'
|
||||||
import { TooltipChild } from '../TooltipChild'
|
|
||||||
import { DisplaySigner } from '../DisplaySigner'
|
import { DisplaySigner } from '../DisplaySigner'
|
||||||
import { Meta } from '../../types'
|
import { Meta } from '../../types'
|
||||||
import { extractFileExtensions } from '../../utils/file'
|
import { extractFileExtensions } from '../../utils/file'
|
||||||
|
import { UserAvatar } from '../UserAvatar'
|
||||||
|
|
||||||
interface UsersDetailsProps {
|
interface UsersDetailsProps {
|
||||||
meta: Meta
|
meta: Meta
|
||||||
@ -36,6 +34,7 @@ interface UsersDetailsProps {
|
|||||||
export const UsersDetails = ({ meta }: UsersDetailsProps) => {
|
export const UsersDetails = ({ meta }: UsersDetailsProps) => {
|
||||||
const {
|
const {
|
||||||
submittedBy,
|
submittedBy,
|
||||||
|
exportedBy,
|
||||||
signers,
|
signers,
|
||||||
viewers,
|
viewers,
|
||||||
fileHashes,
|
fileHashes,
|
||||||
@ -47,11 +46,6 @@ export const UsersDetails = ({ meta }: UsersDetailsProps) => {
|
|||||||
isValid
|
isValid
|
||||||
} = useSigitMeta(meta)
|
} = useSigitMeta(meta)
|
||||||
const { usersPubkey } = useSelector((state: State) => state.auth)
|
const { usersPubkey } = useSelector((state: State) => state.auth)
|
||||||
const profiles = useSigitProfiles([
|
|
||||||
...(submittedBy ? [submittedBy] : []),
|
|
||||||
...signers,
|
|
||||||
...viewers
|
|
||||||
])
|
|
||||||
const userCanSign =
|
const userCanSign =
|
||||||
typeof usersPubkey !== 'undefined' &&
|
typeof usersPubkey !== 'undefined' &&
|
||||||
signers.includes(hexToNpub(usersPubkey))
|
signers.includes(hexToNpub(usersPubkey))
|
||||||
@ -63,31 +57,12 @@ export const UsersDetails = ({ meta }: UsersDetailsProps) => {
|
|||||||
<div className={styles.section}>
|
<div className={styles.section}>
|
||||||
<p>Signers</p>
|
<p>Signers</p>
|
||||||
<div className={styles.users}>
|
<div className={styles.users}>
|
||||||
{submittedBy &&
|
{submittedBy && (
|
||||||
(function () {
|
|
||||||
const profile = profiles[submittedBy]
|
|
||||||
return (
|
|
||||||
<Tooltip
|
|
||||||
key={submittedBy}
|
|
||||||
title={
|
|
||||||
profile?.display_name ||
|
|
||||||
profile?.name ||
|
|
||||||
shorten(hexToNpub(submittedBy))
|
|
||||||
}
|
|
||||||
placement="top"
|
|
||||||
arrow
|
|
||||||
disableInteractive
|
|
||||||
>
|
|
||||||
<TooltipChild>
|
|
||||||
<DisplaySigner
|
<DisplaySigner
|
||||||
status={isValid ? SignStatus.Signed : SignStatus.Invalid}
|
status={isValid ? SignStatus.Signed : SignStatus.Invalid}
|
||||||
profile={profile}
|
|
||||||
pubkey={submittedBy}
|
pubkey={submittedBy}
|
||||||
/>
|
/>
|
||||||
</TooltipChild>
|
)}
|
||||||
</Tooltip>
|
|
||||||
)
|
|
||||||
})()}
|
|
||||||
|
|
||||||
{submittedBy && signers.length ? (
|
{submittedBy && signers.length ? (
|
||||||
<Divider orientation="vertical" flexItem />
|
<Divider orientation="vertical" flexItem />
|
||||||
@ -96,26 +71,8 @@ export const UsersDetails = ({ meta }: UsersDetailsProps) => {
|
|||||||
<UserAvatarGroup max={20}>
|
<UserAvatarGroup max={20}>
|
||||||
{signers.map((signer) => {
|
{signers.map((signer) => {
|
||||||
const pubkey = npubToHex(signer)!
|
const pubkey = npubToHex(signer)!
|
||||||
const profile = profiles[pubkey]
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Tooltip
|
<DisplaySigner status={signersStatus[signer]} pubkey={pubkey} />
|
||||||
key={signer}
|
|
||||||
title={
|
|
||||||
profile?.display_name || profile?.name || shorten(pubkey)
|
|
||||||
}
|
|
||||||
placement="top"
|
|
||||||
arrow
|
|
||||||
disableInteractive
|
|
||||||
>
|
|
||||||
<TooltipChild>
|
|
||||||
<DisplaySigner
|
|
||||||
status={signersStatus[signer]}
|
|
||||||
profile={profile}
|
|
||||||
pubkey={pubkey}
|
|
||||||
/>
|
|
||||||
</TooltipChild>
|
|
||||||
</Tooltip>
|
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
</UserAvatarGroup>
|
</UserAvatarGroup>
|
||||||
@ -128,34 +85,24 @@ export const UsersDetails = ({ meta }: UsersDetailsProps) => {
|
|||||||
<UserAvatarGroup max={20}>
|
<UserAvatarGroup max={20}>
|
||||||
{viewers.map((signer) => {
|
{viewers.map((signer) => {
|
||||||
const pubkey = npubToHex(signer)!
|
const pubkey = npubToHex(signer)!
|
||||||
const profile = profiles[pubkey]
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Tooltip
|
<DisplaySigner status={SignStatus.Viewer} pubkey={pubkey} />
|
||||||
key={signer}
|
|
||||||
title={
|
|
||||||
profile?.display_name ||
|
|
||||||
profile?.name ||
|
|
||||||
shorten(pubkey)
|
|
||||||
}
|
|
||||||
placement="top"
|
|
||||||
arrow
|
|
||||||
disableInteractive
|
|
||||||
>
|
|
||||||
<TooltipChild>
|
|
||||||
<DisplaySigner
|
|
||||||
status={SignStatus.Viewer}
|
|
||||||
profile={profile}
|
|
||||||
pubkey={pubkey}
|
|
||||||
/>
|
|
||||||
</TooltipChild>
|
|
||||||
</Tooltip>
|
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
</UserAvatarGroup>
|
</UserAvatarGroup>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{exportedBy && (
|
||||||
|
<>
|
||||||
|
<p>Exported By</p>
|
||||||
|
<div className={styles.users}>
|
||||||
|
<UserAvatar pubkey={exportedBy} />
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.section}>
|
<div className={styles.section}>
|
||||||
<p>Details</p>
|
<p>Details</p>
|
||||||
|
46
src/hooks/useProfileMetadata.tsx
Normal file
46
src/hooks/useProfileMetadata.tsx
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
import { useEffect, useState } from 'react'
|
||||||
|
import { ProfileMetadata } from '../types/profile'
|
||||||
|
import { MetadataController } from '../controllers/MetadataController'
|
||||||
|
import { Event, kinds } from 'nostr-tools'
|
||||||
|
|
||||||
|
export const useProfileMetadata = (pubkey: string) => {
|
||||||
|
const [profileMetadata, setProfileMetadata] = useState<ProfileMetadata>()
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const metadataController = MetadataController.getInstance()
|
||||||
|
const handleMetadataEvent = (event: Event) => {
|
||||||
|
const metadataContent =
|
||||||
|
metadataController.extractProfileMetadataContent(event)
|
||||||
|
|
||||||
|
if (metadataContent) {
|
||||||
|
setProfileMetadata(metadataContent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pubkey) {
|
||||||
|
metadataController.on(pubkey, (kind: number, event: Event) => {
|
||||||
|
if (kind === kinds.Metadata) {
|
||||||
|
handleMetadataEvent(event)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
metadataController
|
||||||
|
.findMetadata(pubkey)
|
||||||
|
.then((metadataEvent) => {
|
||||||
|
if (metadataEvent) handleMetadataEvent(metadataEvent)
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.error(
|
||||||
|
`error occurred in finding metadata for: ${pubkey}`,
|
||||||
|
err
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
metadataController.off(pubkey, handleMetadataEvent)
|
||||||
|
}
|
||||||
|
}, [pubkey])
|
||||||
|
|
||||||
|
return profileMetadata
|
||||||
|
}
|
@ -1,71 +0,0 @@
|
|||||||
import { useEffect, useState } from 'react'
|
|
||||||
import { ProfileMetadata } from '../types'
|
|
||||||
import { MetadataController } from '../controllers'
|
|
||||||
import { npubToHex } from '../utils'
|
|
||||||
import { Event, kinds } from 'nostr-tools'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Extracts profiles from metadata events
|
|
||||||
* @param pubkeys Array of npubs to check
|
|
||||||
* @returns ProfileMetadata
|
|
||||||
*/
|
|
||||||
export const useSigitProfiles = (
|
|
||||||
pubkeys: `npub1${string}`[]
|
|
||||||
): { [key: string]: ProfileMetadata } => {
|
|
||||||
const [profileMetadata, setProfileMetadata] = useState<{
|
|
||||||
[key: string]: ProfileMetadata
|
|
||||||
}>({})
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (pubkeys.length) {
|
|
||||||
const metadataController = new MetadataController()
|
|
||||||
|
|
||||||
// Remove duplicate keys
|
|
||||||
const users = new Set<string>([...pubkeys])
|
|
||||||
|
|
||||||
const handleMetadataEvent = (key: string) => (event: Event) => {
|
|
||||||
const metadataContent =
|
|
||||||
metadataController.extractProfileMetadataContent(event)
|
|
||||||
|
|
||||||
if (metadataContent) {
|
|
||||||
setProfileMetadata((prev) => ({
|
|
||||||
...prev,
|
|
||||||
[key]: metadataContent
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
users.forEach((user) => {
|
|
||||||
const hexKey = npubToHex(user)
|
|
||||||
if (hexKey && !(hexKey in profileMetadata)) {
|
|
||||||
metadataController.on(hexKey, (kind: number, event: Event) => {
|
|
||||||
if (kind === kinds.Metadata) {
|
|
||||||
handleMetadataEvent(hexKey)(event)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
metadataController
|
|
||||||
.findMetadata(hexKey)
|
|
||||||
.then((metadataEvent) => {
|
|
||||||
if (metadataEvent) handleMetadataEvent(hexKey)(metadataEvent)
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
console.error(
|
|
||||||
`error occurred in finding metadata for: ${user}`,
|
|
||||||
err
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
users.forEach((key) => {
|
|
||||||
metadataController.off(key, handleMetadataEvent(key))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, [pubkeys])
|
|
||||||
|
|
||||||
return profileMetadata
|
|
||||||
}
|
|
@ -36,7 +36,6 @@ import {
|
|||||||
npubToHex,
|
npubToHex,
|
||||||
queryNip05,
|
queryNip05,
|
||||||
sendNotification,
|
sendNotification,
|
||||||
shorten,
|
|
||||||
signEventForMetaFile,
|
signEventForMetaFile,
|
||||||
updateUsersAppData,
|
updateUsersAppData,
|
||||||
uploadToFileStorage
|
uploadToFileStorage
|
||||||
@ -113,6 +112,8 @@ export const CreatePage = () => {
|
|||||||
const [error, setError] = useState<string>()
|
const [error, setError] = useState<string>()
|
||||||
|
|
||||||
const [users, setUsers] = useState<User[]>([])
|
const [users, setUsers] = useState<User[]>([])
|
||||||
|
const signers = users.filter((u) => u.role === UserRole.signer)
|
||||||
|
const viewers = users.filter((u) => u.role === UserRole.viewer)
|
||||||
|
|
||||||
const usersPubkey = useSelector((state: State) => state.auth.usersPubkey)
|
const usersPubkey = useSelector((state: State) => state.auth.usersPubkey)
|
||||||
|
|
||||||
@ -252,7 +253,7 @@ export const CreatePage = () => {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
users.forEach((user) => {
|
users.forEach((user) => {
|
||||||
if (!(user.pubkey in metadata)) {
|
if (!(user.pubkey in metadata)) {
|
||||||
const metadataController = new MetadataController()
|
const metadataController = MetadataController.getInstance()
|
||||||
|
|
||||||
const handleMetadataEvent = (event: Event) => {
|
const handleMetadataEvent = (event: Event) => {
|
||||||
const metadataContent =
|
const metadataContent =
|
||||||
@ -647,6 +648,11 @@ export const CreatePage = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
saveAs(finalZipFile, `request-${unixNow()}.sigit.zip`)
|
saveAs(finalZipFile, `request-${unixNow()}.sigit.zip`)
|
||||||
|
|
||||||
|
// If user is the next signer, we can navigate directly to sign page
|
||||||
|
if (signers[0].pubkey === usersPubkey) {
|
||||||
|
navigate(appPrivateRoutes.sign, { state: { uploadedZip: finalZipFile } })
|
||||||
|
}
|
||||||
setIsLoading(false)
|
setIsLoading(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -672,9 +678,6 @@ export const CreatePage = () => {
|
|||||||
},
|
},
|
||||||
zipUrl: string
|
zipUrl: string
|
||||||
) => {
|
) => {
|
||||||
const signers = users.filter((user) => user.role === UserRole.signer)
|
|
||||||
const viewers = users.filter((user) => user.role === UserRole.viewer)
|
|
||||||
|
|
||||||
const content: CreateSignatureEventContent = {
|
const content: CreateSignatureEventContent = {
|
||||||
signers: signers.map((signer) => hexToNpub(signer.pubkey)),
|
signers: signers.map((signer) => hexToNpub(signer.pubkey)),
|
||||||
viewers: viewers.map((viewer) => hexToNpub(viewer.pubkey)),
|
viewers: viewers.map((viewer) => hexToNpub(viewer.pubkey)),
|
||||||
@ -703,9 +706,6 @@ export const CreatePage = () => {
|
|||||||
|
|
||||||
// Send notifications to signers and viewers
|
// Send notifications to signers and viewers
|
||||||
const sendNotifications = (meta: Meta) => {
|
const sendNotifications = (meta: Meta) => {
|
||||||
const signers = users.filter((user) => user.role === UserRole.signer)
|
|
||||||
const viewers = users.filter((user) => user.role === UserRole.viewer)
|
|
||||||
|
|
||||||
// no need to send notification to self so remove it from the list
|
// no need to send notification to self so remove it from the list
|
||||||
const receivers = (
|
const receivers = (
|
||||||
signers.length > 0
|
signers.length > 0
|
||||||
@ -787,7 +787,7 @@ export const CreatePage = () => {
|
|||||||
toast.error('Failed to publish notifications')
|
toast.error('Failed to publish notifications')
|
||||||
})
|
})
|
||||||
|
|
||||||
navigate(appPrivateRoutes.sign, { state: { meta: meta } })
|
navigate(appPrivateRoutes.sign, { state: { meta } })
|
||||||
} else {
|
} else {
|
||||||
const zip = new JSZip()
|
const zip = new JSZip()
|
||||||
|
|
||||||
@ -914,7 +914,6 @@ export const CreatePage = () => {
|
|||||||
<div className={styles.flexWrap}>
|
<div className={styles.flexWrap}>
|
||||||
<div className={`${styles.paperGroup} ${styles.users}`}>
|
<div className={`${styles.paperGroup} ${styles.users}`}>
|
||||||
<DisplayUser
|
<DisplayUser
|
||||||
metadata={metadata}
|
|
||||||
users={users}
|
users={users}
|
||||||
handleUserRoleChange={handleUserRoleChange}
|
handleUserRoleChange={handleUserRoleChange}
|
||||||
handleRemoveUser={handleRemoveUser}
|
handleRemoveUser={handleRemoveUser}
|
||||||
@ -1010,7 +1009,6 @@ export const CreatePage = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type DisplayUsersProps = {
|
type DisplayUsersProps = {
|
||||||
metadata: { [key: string]: ProfileMetadata }
|
|
||||||
users: User[]
|
users: User[]
|
||||||
handleUserRoleChange: (role: UserRole, pubkey: string) => void
|
handleUserRoleChange: (role: UserRole, pubkey: string) => void
|
||||||
handleRemoveUser: (pubkey: string) => void
|
handleRemoveUser: (pubkey: string) => void
|
||||||
@ -1018,7 +1016,6 @@ type DisplayUsersProps = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const DisplayUser = ({
|
const DisplayUser = ({
|
||||||
metadata,
|
|
||||||
users,
|
users,
|
||||||
handleUserRoleChange,
|
handleUserRoleChange,
|
||||||
handleRemoveUser,
|
handleRemoveUser,
|
||||||
@ -1032,7 +1029,6 @@ const DisplayUser = ({
|
|||||||
.map((user, index) => (
|
.map((user, index) => (
|
||||||
<SignerCounterpart
|
<SignerCounterpart
|
||||||
key={`signer-${user.pubkey}`}
|
key={`signer-${user.pubkey}`}
|
||||||
userMeta={metadata[user.pubkey]}
|
|
||||||
user={user}
|
user={user}
|
||||||
index={index}
|
index={index}
|
||||||
moveSigner={moveSigner}
|
moveSigner={moveSigner}
|
||||||
@ -1047,7 +1043,6 @@ const DisplayUser = ({
|
|||||||
return (
|
return (
|
||||||
<div className={styles.user} key={`viewer-${user.pubkey}`}>
|
<div className={styles.user} key={`viewer-${user.pubkey}`}>
|
||||||
<Counterpart
|
<Counterpart
|
||||||
userMeta={metadata[user.pubkey]}
|
|
||||||
user={user}
|
user={user}
|
||||||
handleUserRoleChange={handleUserRoleChange}
|
handleUserRoleChange={handleUserRoleChange}
|
||||||
handleRemoveUser={handleRemoveUser}
|
handleRemoveUser={handleRemoveUser}
|
||||||
@ -1066,7 +1061,6 @@ interface DragItem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type CounterpartProps = {
|
type CounterpartProps = {
|
||||||
userMeta: ProfileMetadata
|
|
||||||
user: User
|
user: User
|
||||||
handleUserRoleChange: (role: UserRole, pubkey: string) => void
|
handleUserRoleChange: (role: UserRole, pubkey: string) => void
|
||||||
handleRemoveUser: (pubkey: string) => void
|
handleRemoveUser: (pubkey: string) => void
|
||||||
@ -1078,7 +1072,6 @@ type SignerCounterpartProps = CounterpartProps & {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const SignerCounterpart = ({
|
const SignerCounterpart = ({
|
||||||
userMeta,
|
|
||||||
user,
|
user,
|
||||||
index,
|
index,
|
||||||
moveSigner,
|
moveSigner,
|
||||||
@ -1171,7 +1164,6 @@ const SignerCounterpart = ({
|
|||||||
<FontAwesomeIcon width={'14px'} fontSize={'14px'} icon={faGripLines} />
|
<FontAwesomeIcon width={'14px'} fontSize={'14px'} icon={faGripLines} />
|
||||||
<Counterpart
|
<Counterpart
|
||||||
user={user}
|
user={user}
|
||||||
userMeta={userMeta}
|
|
||||||
handleRemoveUser={handleRemoveUser}
|
handleRemoveUser={handleRemoveUser}
|
||||||
handleUserRoleChange={handleUserRoleChange}
|
handleUserRoleChange={handleUserRoleChange}
|
||||||
/>
|
/>
|
||||||
@ -1180,7 +1172,6 @@ const SignerCounterpart = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const Counterpart = ({
|
const Counterpart = ({
|
||||||
userMeta,
|
|
||||||
user,
|
user,
|
||||||
handleUserRoleChange,
|
handleUserRoleChange,
|
||||||
handleRemoveUser
|
handleRemoveUser
|
||||||
@ -1188,15 +1179,7 @@ const Counterpart = ({
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className={styles.avatar}>
|
<div className={styles.avatar}>
|
||||||
<UserAvatar
|
<UserAvatar pubkey={user.pubkey} />
|
||||||
pubkey={user.pubkey}
|
|
||||||
name={
|
|
||||||
userMeta?.display_name ||
|
|
||||||
userMeta?.name ||
|
|
||||||
shorten(hexToNpub(user.pubkey))
|
|
||||||
}
|
|
||||||
image={userMeta?.picture}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
<Tooltip title="Toggle User Role" arrow disableInteractive>
|
<Tooltip title="Toggle User Role" arrow disableInteractive>
|
||||||
<Button
|
<Button
|
||||||
|
@ -32,7 +32,7 @@ import { useState, useEffect } from 'react'
|
|||||||
import { toast } from 'react-toastify'
|
import { toast } from 'react-toastify'
|
||||||
import { UserAvatar } from '../../../components/UserAvatar'
|
import { UserAvatar } from '../../../components/UserAvatar'
|
||||||
import { MetadataController } from '../../../controllers'
|
import { MetadataController } from '../../../controllers'
|
||||||
import { npubToHex, shorten, hexToNpub, parseJson } from '../../../utils'
|
import { npubToHex, hexToNpub, parseJson } from '../../../utils'
|
||||||
import styles from '../style.module.scss'
|
import styles from '../style.module.scss'
|
||||||
import { SigitFile } from '../../../utils/file'
|
import { SigitFile } from '../../../utils/file'
|
||||||
|
|
||||||
@ -105,7 +105,7 @@ export const DisplayMeta = ({
|
|||||||
}, [signers, viewers])
|
}, [signers, viewers])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const metadataController = new MetadataController()
|
const metadataController = MetadataController.getInstance()
|
||||||
|
|
||||||
const hexKeys: string[] = [
|
const hexKeys: string[] = [
|
||||||
npubToHex(submittedBy)!,
|
npubToHex(submittedBy)!,
|
||||||
@ -167,20 +167,7 @@ export const DisplayMeta = ({
|
|||||||
<Typography variant="h6" sx={{ color: textColor }}>
|
<Typography variant="h6" sx={{ color: textColor }}>
|
||||||
Submitted By
|
Submitted By
|
||||||
</Typography>
|
</Typography>
|
||||||
{(function () {
|
<UserAvatar pubkey={submittedBy} isNameVisible={true} />
|
||||||
const profile = metadata[submittedBy]
|
|
||||||
return (
|
|
||||||
<UserAvatar
|
|
||||||
pubkey={submittedBy}
|
|
||||||
name={
|
|
||||||
profile?.display_name ||
|
|
||||||
profile?.name ||
|
|
||||||
shorten(hexToNpub(submittedBy))
|
|
||||||
}
|
|
||||||
image={profile?.picture}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
})()}
|
|
||||||
</ListItem>
|
</ListItem>
|
||||||
<ListItem
|
<ListItem
|
||||||
sx={{
|
sx={{
|
||||||
@ -280,14 +267,12 @@ type DisplayUserProps = {
|
|||||||
const DisplayUser = ({
|
const DisplayUser = ({
|
||||||
meta,
|
meta,
|
||||||
user,
|
user,
|
||||||
metadata,
|
|
||||||
signedBy,
|
signedBy,
|
||||||
nextSigner,
|
nextSigner,
|
||||||
getPrevSignersSig
|
getPrevSignersSig
|
||||||
}: DisplayUserProps) => {
|
}: DisplayUserProps) => {
|
||||||
const theme = useTheme()
|
const theme = useTheme()
|
||||||
|
|
||||||
const userMeta = metadata[user.pubkey]
|
|
||||||
const [userStatus, setUserStatus] = useState<UserStatus>(UserStatus.Pending)
|
const [userStatus, setUserStatus] = useState<UserStatus>(UserStatus.Pending)
|
||||||
const [prevSignatureStatus, setPreviousSignatureStatus] =
|
const [prevSignatureStatus, setPreviousSignatureStatus] =
|
||||||
useState<PrevSignatureValidationEnum>(PrevSignatureValidationEnum.Pending)
|
useState<PrevSignatureValidationEnum>(PrevSignatureValidationEnum.Pending)
|
||||||
@ -370,15 +355,7 @@ const DisplayUser = ({
|
|||||||
return (
|
return (
|
||||||
<TableRow>
|
<TableRow>
|
||||||
<TableCell className={styles.tableCell}>
|
<TableCell className={styles.tableCell}>
|
||||||
<UserAvatar
|
<UserAvatar pubkey={user.pubkey} />
|
||||||
pubkey={user.pubkey}
|
|
||||||
name={
|
|
||||||
userMeta?.display_name ||
|
|
||||||
userMeta?.name ||
|
|
||||||
shorten(hexToNpub(user.pubkey))
|
|
||||||
}
|
|
||||||
image={userMeta?.picture}
|
|
||||||
/>
|
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell className={styles.tableCell}>{user.role}</TableCell>
|
<TableCell className={styles.tableCell}>{user.role}</TableCell>
|
||||||
<TableCell>
|
<TableCell>
|
||||||
|
Loading…
Reference in New Issue
Block a user