feat: show block number on user profile #18

Merged
b merged 3 commits from issue-1 into main 2024-05-10 10:58:15 +00:00
3 changed files with 39 additions and 17 deletions
Showing only changes of commit 37bc205ce4 - Show all commits

View File

@ -6,9 +6,10 @@ import {
validateEvent, validateEvent,
verifyEvent, verifyEvent,
Event, Event,
EventTemplate EventTemplate,
nip19
} from 'nostr-tools' } from 'nostr-tools'
import { ProfileMetadata, RelaySet } from '../types' import { NostrJoiningBlock, ProfileMetadata, RelaySet } from '../types'
import { NostrController } from '.' import { NostrController } from '.'
import { toast } from 'react-toastify' import { toast } from 'react-toastify'
import { queryNip05 } from '../utils' import { queryNip05 } from '../utils'
@ -166,14 +167,16 @@ export class MetadataController {
}) })
} }
public getNostrJoiningBlockNumber = async (hexKey: string) => { public getNostrJoiningBlockNumber = async (
hexKey: string
): Promise<NostrJoiningBlock | null> => {
const relaySet = await this.findRelayListMetadata(hexKey) const relaySet = await this.findRelayListMetadata(hexKey)
const relays: string[] = [] const userRelays: string[] = []
// find user's relays // find user's relays
if (relaySet.write.length > 0) { if (relaySet.write.length > 0) {
relays.push(...relaySet.write) userRelays.push(...relaySet.write)
} else { } else {
const metadata = await this.findMetadata(hexKey) const metadata = await this.findMetadata(hexKey)
const metadataContent = this.extractProfileMetadataContent(metadata) const metadataContent = this.extractProfileMetadataContent(metadata)
@ -182,12 +185,12 @@ export class MetadataController {
const nip05Profile = await queryNip05(metadataContent.nip05) const nip05Profile = await queryNip05(metadataContent.nip05)
if (nip05Profile && nip05Profile.pubkey === hexKey) { if (nip05Profile && nip05Profile.pubkey === hexKey) {
relays.push(...nip05Profile.relays) userRelays.push(...nip05Profile.relays)
} }
} }
} }
if (relays.length === 0) return null if (userRelays.length === 0) return null
// filter for finding user's first kind 1 event // filter for finding user's first kind 1 event
const eventFilter: Filter = { const eventFilter: Filter = {
@ -198,7 +201,7 @@ export class MetadataController {
const pool = new SimplePool() const pool = new SimplePool()
// find user's kind 1 events published on user's relays // find user's kind 1 events published on user's relays
const events = await pool.querySync(relays, eventFilter) const events = await pool.querySync(userRelays, eventFilter)
if (events && events.length) { if (events && events.length) {
// sort events by created_at time in ascending order // sort events by created_at time in ascending order
events.sort((a, b) => a.created_at - b.created_at) events.sort((a, b) => a.created_at - b.created_at)
@ -270,10 +273,20 @@ export class MetadataController {
'#p': [jobSignedEvent.pubkey] '#p': [jobSignedEvent.pubkey]
}) })
// asynchronously get block number from dvm job with 10 seconds timeout // asynchronously get block number from dvm job with 20 seconds timeout
const dvmJobResult = await subscribeWithTimeout(sub, 10000) const dvmJobResult = await subscribeWithTimeout(sub, 20000)
return parseInt(dvmJobResult) const encodedEventPointer = nip19.neventEncode({
id: event.id,
relays: userRelays,
author: event.pubkey,
kind: event.kind
})
return {
block: parseInt(dvmJobResult),
encodedEventPointer
}
} }
return null return null

View File

@ -9,11 +9,11 @@ import {
} from '@mui/material' } from '@mui/material'
import { UnsignedEvent, nip19, kinds, VerifiedEvent } from 'nostr-tools' import { UnsignedEvent, nip19, kinds, VerifiedEvent } from 'nostr-tools'
import { useEffect, useMemo, useState } from 'react' import { useEffect, useMemo, useState } from 'react'
import { useParams } from 'react-router-dom' import { Link, useParams } from 'react-router-dom'
import { toast } from 'react-toastify' import { toast } from 'react-toastify'
import placeholderAvatar from '../../assets/images/nostr-logo.jpg' import placeholderAvatar from '../../assets/images/nostr-logo.jpg'
import { MetadataController, NostrController } from '../../controllers' import { MetadataController, NostrController } from '../../controllers'
import { ProfileMetadata } from '../../types' import { NostrJoiningBlock, ProfileMetadata } from '../../types'
import styles from './style.module.scss' import styles from './style.module.scss'
import { useDispatch, useSelector } from 'react-redux' import { useDispatch, useSelector } from 'react-redux'
import { State } from '../../store/rootReducer' import { State } from '../../store/rootReducer'
@ -34,7 +34,8 @@ export const ProfilePage = () => {
const nostrController = NostrController.getInstance() const nostrController = NostrController.getInstance()
const [pubkey, setPubkey] = useState<string>() const [pubkey, setPubkey] = useState<string>()
const [blockNumber, setBlockNumber] = useState<number | null>(null) const [nostrJoiningBlock, setNostrJoiningBlock] =
useState<NostrJoiningBlock | null>(null)
const [profileMetadata, setProfileMetadata] = useState<ProfileMetadata>() const [profileMetadata, setProfileMetadata] = useState<ProfileMetadata>()
const [savingProfileMetadata, setSavingProfileMetadata] = useState(false) const [savingProfileMetadata, setSavingProfileMetadata] = useState(false)
const metadataState = useSelector((state: State) => state.metadata) const metadataState = useSelector((state: State) => state.metadata)
@ -64,7 +65,7 @@ export const ProfilePage = () => {
metadataController metadataController
.getNostrJoiningBlockNumber(pubkey) .getNostrJoiningBlockNumber(pubkey)
.then((res) => { .then((res) => {
setBlockNumber(res) setNostrJoiningBlock(res)
}) })
.catch((err) => { .catch((err) => {
// todo: handle error // todo: handle error
@ -239,15 +240,18 @@ export const ProfilePage = () => {
src={profileMetadata.picture || placeholderAvatar} src={profileMetadata.picture || placeholderAvatar}
alt='Profile Image' alt='Profile Image'
/> />
{blockNumber && ( {nostrJoiningBlock && (
<Typography <Typography
sx={{ sx={{
color: theme.palette.getContrastText( color: theme.palette.getContrastText(
theme.palette.background.paper theme.palette.background.paper
) )
}} }}
component={Link}
to={`https://njump.me/${nostrJoiningBlock.encodedEventPointer}`}
target='_blank'
> >
On nostr since {blockNumber.toLocaleString()} On nostr since {nostrJoiningBlock.block.toLocaleString()}
</Typography> </Typography>
)} )}
</ListItem> </ListItem>

View File

@ -12,3 +12,8 @@ export interface RelaySet {
read: string[] read: string[]
write: string[] write: string[]
} }
export interface NostrJoiningBlock {
block: number
encodedEventPointer: string
}