release #111
@ -1,5 +1,3 @@
|
||||
import { UnsignedEvent } from 'nostr-tools'
|
||||
|
||||
export interface SignedEvent {
|
||||
kind: number
|
||||
tags: string[][]
|
||||
@ -14,5 +12,3 @@ export interface NostrJoiningBlock {
|
||||
block: number
|
||||
encodedEventPointer: string
|
||||
}
|
||||
|
||||
export type Rumor = UnsignedEvent & { id: string }
|
||||
|
@ -8,13 +8,12 @@ import {
|
||||
verifyEvent
|
||||
} from 'nostr-tools'
|
||||
import { toast } from 'react-toastify'
|
||||
import { MetadataController, NostrController } from '../controllers'
|
||||
import { appPrivateRoutes } from '../routes'
|
||||
import { NostrController } from '../controllers'
|
||||
import { AuthState } from '../store/auth/types'
|
||||
import store from '../store/store'
|
||||
import { CreateSignatureEventContent, Meta } from '../types'
|
||||
import { hexToNpub, now } from './nostr'
|
||||
import { parseJson } from './string'
|
||||
import { CreateSignatureEventContent, Meta } from '../types'
|
||||
import store from '../store/store'
|
||||
import { AuthState } from '../store/auth/types'
|
||||
|
||||
/**
|
||||
* Uploads a file to a file storage service.
|
||||
@ -57,128 +56,6 @@ export const uploadToFileStorage = async (
|
||||
return response.data.url as string
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a Direct Message (DM) to a recipient, encrypting the content and handling authentication.
|
||||
* @param fileUrl The URL of the encrypted zip file to be included in the DM.
|
||||
* @param encryptionKey The encryption key used to decrypt the zip file to be included in the DM.
|
||||
* @param pubkey The public key of the recipient.
|
||||
* @param nostrController The NostrController instance for handling authentication and encryption.
|
||||
* @param isSigner Boolean indicating whether the recipient is a signer or viewer.
|
||||
* @param setAuthUrl Function to set the authentication URL in the component state.
|
||||
*/
|
||||
export const sendDM = async (
|
||||
fileUrl: string,
|
||||
encryptionKey: string,
|
||||
pubkey: string,
|
||||
nostrController: NostrController,
|
||||
isSigner: boolean,
|
||||
setAuthUrl: (value: React.SetStateAction<string | undefined>) => void
|
||||
) => {
|
||||
// Construct the content of the DM
|
||||
const initialLine = isSigner
|
||||
? 'Your signature is requested on the document below!'
|
||||
: 'You have received a signed document.'
|
||||
|
||||
const decryptionUrl = `${window.location.origin}/#${
|
||||
appPrivateRoutes.sign
|
||||
}?file=${encodeURIComponent(fileUrl)}&key=${encodeURIComponent(
|
||||
encryptionKey
|
||||
)}`
|
||||
|
||||
const content = `${initialLine}\n\n${decryptionUrl}`
|
||||
|
||||
// Set up event listener for authentication event
|
||||
nostrController.on('nsecbunker-auth', (url) => {
|
||||
setAuthUrl(url)
|
||||
})
|
||||
|
||||
// Set up timeout promise to handle encryption timeout
|
||||
const timeoutPromise = new Promise<never>((_, reject) => {
|
||||
setTimeout(() => {
|
||||
reject(new Error('Timeout occurred'))
|
||||
}, 60000) // Timeout duration = 60 seconds
|
||||
})
|
||||
|
||||
// Encrypt the DM content, with timeout
|
||||
const encrypted = await Promise.race([
|
||||
nostrController.nip04Encrypt(pubkey, content),
|
||||
timeoutPromise
|
||||
])
|
||||
.then((res) => {
|
||||
return res
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log('err :>> ', err)
|
||||
toast.error(
|
||||
err.message || 'An error occurred while encrypting DM content'
|
||||
)
|
||||
return null
|
||||
})
|
||||
.finally(() => {
|
||||
setAuthUrl(undefined) // Clear authentication URL
|
||||
})
|
||||
|
||||
// Return if encryption failed
|
||||
if (!encrypted) return
|
||||
|
||||
// Construct event metadata for the DM
|
||||
const event: EventTemplate = {
|
||||
kind: 4, // DM event type
|
||||
content: encrypted, // Encrypted DM content
|
||||
created_at: Math.floor(Date.now() / 1000), // Current timestamp
|
||||
tags: [['p', pubkey]] // Tag with recipient's public key
|
||||
}
|
||||
|
||||
// Sign the DM event
|
||||
const signedEvent = await nostrController.signEvent(event).catch((err) => {
|
||||
console.log('err :>> ', err)
|
||||
toast.error(err.message || 'An error occurred while signing event for DM')
|
||||
return null
|
||||
})
|
||||
|
||||
// Return if event signing failed
|
||||
if (!signedEvent) return
|
||||
|
||||
// Get relay list metadata
|
||||
const metadataController = new MetadataController()
|
||||
const relaySet = await metadataController
|
||||
.findRelayListMetadata(pubkey)
|
||||
.catch((err) => {
|
||||
toast.error(
|
||||
err.message || 'An error occurred while finding relay list metadata'
|
||||
)
|
||||
return null
|
||||
})
|
||||
|
||||
// Return if metadata retrieval failed
|
||||
if (!relaySet) return
|
||||
|
||||
// Ensure relay list is not empty
|
||||
if (relaySet.read.length === 0) {
|
||||
toast.error('No relay found for publishing encrypted DM')
|
||||
return
|
||||
}
|
||||
|
||||
// Publish the signed DM event to the recipient's read relays
|
||||
await nostrController
|
||||
.publishEvent(signedEvent, relaySet.read)
|
||||
.then((relays) => {
|
||||
toast.success(`Encrypted DM sent on: ${relays.join('\n')}`)
|
||||
})
|
||||
.catch((errResults) => {
|
||||
console.log('err :>> ', errResults)
|
||||
toast.error('An error occurred while publishing DM')
|
||||
|
||||
errResults.forEach((errResult: any) => {
|
||||
toast.error(
|
||||
`Publishing to ${errResult.relay} caused the following error: ${errResult.error}`
|
||||
)
|
||||
})
|
||||
|
||||
return null
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Signs an event for a meta.json file.
|
||||
* @param content contains content for event.
|
||||
|
@ -209,10 +209,7 @@ export const getRoboHashPicture = (
|
||||
return `https://robohash.org/${npub}.png?set=set${set}`
|
||||
}
|
||||
|
||||
export const TWO_DAYS = 2 * 24 * 60 * 60
|
||||
|
||||
export const now = () => Math.round(Date.now() / 1000)
|
||||
export const randomNow = () => Math.round(now() - Math.random() * TWO_DAYS)
|
||||
|
||||
/**
|
||||
* Generate nip44 conversation key
|
||||
|
Loading…
Reference in New Issue
Block a user