fix: addressing comments

This commit is contained in:
Stixx 2025-01-15 10:38:40 +01:00
parent 6777e96d23
commit b7d1fea077
12 changed files with 95 additions and 264 deletions

View File

@ -13,6 +13,13 @@ import { MarkRenderSignature } from './Render'
export const SignatureStrategy: MarkStrategy = { export const SignatureStrategy: MarkStrategy = {
input: MarkInputSignature, input: MarkInputSignature,
render: MarkRenderSignature, render: MarkRenderSignature,
/**
* Encrypts a stringified signature object, creates an encrypted JSON file,
* and uploads it to a file storage if the user is online.
* @param value
* @param encryptionKey
* @returns the original value string
*/
encryptAndUpload: async (value, encryptionKey) => { encryptAndUpload: async (value, encryptionKey) => {
// Value is the stringified signature object // Value is the stringified signature object
// Encode it to the arrayBuffer // Encode it to the arrayBuffer
@ -43,8 +50,6 @@ export const SignatureStrategy: MarkStrategy = {
console.info( console.info(
`${file.name} uploaded to following file storages: ${urls.join(', ')}` `${file.name} uploaded to following file storages: ${urls.join(', ')}`
) )
// This bit was returning an url, and return of this function is being set to mark.value, so it kind of
// does not make sense to return an url to the file storage
return value return value
} catch (error) { } catch (error) {
if (error instanceof Error) { if (error instanceof Error) {

View File

@ -26,6 +26,11 @@ import styles from '../UsersDetails.tsx/style.module.scss'
interface PdfMarkingProps { interface PdfMarkingProps {
currentUserMarks: CurrentUserMark[] currentUserMarks: CurrentUserMark[]
files: CurrentUserFile[] files: CurrentUserFile[]
/**
* Currently, loading spinner is present if `files` array is of length 0,
* Which means if no files are found, loading spinner will be spinning indefinitely
* For that reason `noFiles` is introduced to set the loading off when fetching is finished.
*/
noFiles?: boolean noFiles?: boolean
handleExport: () => void handleExport: () => void
handleEncryptedExport: () => void handleEncryptedExport: () => void
@ -179,12 +184,10 @@ const PdfMarking = (props: PdfMarkingProps) => {
currentUserMarks={currentUserMarks} currentUserMarks={currentUserMarks}
otherUserMarks={otherUserMarks} otherUserMarks={otherUserMarks}
/> />
{noFiles ? ( {noFiles && (
<Typography textAlign="center"> <Typography textAlign="center">
We were not able to retrieve the files. We were not able to retrieve the files.
</Typography> </Typography>
) : (
''
)} )}
</StickySideColumns> </StickySideColumns>

View File

@ -10,6 +10,11 @@ interface PdfViewProps {
currentFile: CurrentUserFile | null currentFile: CurrentUserFile | null
currentUserMarks: CurrentUserMark[] currentUserMarks: CurrentUserMark[]
files: CurrentUserFile[] files: CurrentUserFile[]
/**
* Currently, loading spinner is present if `files` array is of length 0,
* Which means if no files are found, loading spinner will be spinning indefinitely
* For that reason `noFiles` is introduced to set the loading off when fetching is finished.
*/
noFiles?: boolean noFiles?: boolean
handleMarkClick: (id: number) => void handleMarkClick: (id: number) => void
otherUserMarks: Mark[] otherUserMarks: Mark[]

View File

@ -95,12 +95,9 @@ export const useAuth = () => {
}) })
) )
const ndkRelayListPromise = getNDKRelayList(pubkey)
const serverMapPromise = getFileServerMap(pubkey, fetchEvent)
const [ndkRelayList, serverMap] = await Promise.all([ const [ndkRelayList, serverMap] = await Promise.all([
ndkRelayListPromise, getNDKRelayList(pubkey),
serverMapPromise getFileServerMap(pubkey, fetchEvent)
]) ])
const relays = ndkRelayList.relays const relays = ndkRelayList.relays

View File

@ -50,15 +50,15 @@ export const ServersPage = () => {
const fetchFileServers = async () => { const fetchFileServers = async () => {
if (usersPubkey) { if (usersPubkey) {
await getFileServerMap(usersPubkey, fetchEvent).then((res) => { const servers = await getFileServerMap(usersPubkey, fetchEvent)
if (res.map) {
if (Object.keys(res.map).length === 0) { if (servers.map) {
if (Object.keys(servers.map).length === 0) {
serverRequirementWarning() serverRequirementWarning()
} }
setBlossomServersMap(res.map) setBlossomServersMap(servers.map)
} }
})
} else { } else {
noUserKeyWarning() noUserKeyWarning()
} }
@ -97,7 +97,7 @@ export const ServersPage = () => {
if (Object.keys(blossomServersMap).includes(serverURL)) if (Object.keys(blossomServersMap).includes(serverURL))
return toast.warning('This server is already added.') return toast.warning('This server is already added.')
const valid = await validateFileServer(serverURL).catch(() => null) const valid = await validateFileServer(serverURL)
if (!valid) if (!valid)
return toast.warning( return toast.warning(
`Server URL ${serverURL} does not seem to be a valid file server.` `Server URL ${serverURL} does not seem to be a valid file server.`
@ -160,18 +160,19 @@ export const ServersPage = () => {
* @param serverURL * @param serverURL
*/ */
const validateFileServer = (serverURL: string) => { const validateFileServer = (serverURL: string) => {
return new Promise((resolve, reject) => { return new Promise((resolve) => {
axios axios
.get(serverURL) .get(serverURL)
.then((res) => { .then((res) => {
if (res && res.data?.toLowerCase().includes('blossom server')) { if (res && res.data?.toLowerCase().includes('blossom server')) {
resolve(true) resolve(true)
} else { } else {
reject(false) resolve(false)
} }
}) })
.catch((err) => { .catch((err) => {
reject(err) console.error('Error validating file server.', err)
resolve(false)
}) })
}) })
} }

View File

@ -169,7 +169,6 @@ export const SignPage = () => {
createSignatureContent.markConfig, createSignatureContent.markConfig,
usersPubkey! usersPubkey!
) )
// TODO figure out why markConfig does not contain the usersPubkey when multiple signer
const signedMarks = extractMarksFromSignedMeta(meta) const signedMarks = extractMarksFromSignedMeta(meta)
const currentUserMarks = getCurrentUserMarks(metaMarks, signedMarks) const currentUserMarks = getCurrentUserMarks(metaMarks, signedMarks)
const otherUserMarks = findOtherUserMarks(signedMarks, usersPubkey!) const otherUserMarks = findOtherUserMarks(signedMarks, usersPubkey!)

View File

@ -63,6 +63,11 @@ import { MarkRender } from '../../components/MarkTypeStrategy/MarkRender.tsx'
interface PdfViewProps { interface PdfViewProps {
files: CurrentUserFile[] files: CurrentUserFile[]
/**
* Currently, loading spinner is present if `files` array is of length 0,
* Which means if no files are found, loading spinner will be spinning indefinitely
* For that reason `noFiles` is introduced to set the loading off when fetching is finished.
*/
noFiles?: boolean noFiles?: boolean
currentFile: CurrentUserFile | null currentFile: CurrentUserFile | null
parsedSignatureEvents: { parsedSignatureEvents: {
@ -460,7 +465,9 @@ export const VerifyPage = () => {
if (!zip) { if (!zip) {
if (!isLastZipUrl) continue // Skip to next zipUrl if (!isLastZipUrl) continue // Skip to next zipUrl
break // If last zipUrl break out of loop // If it's the last zip url, and still no `zip` found, break out of loop,
// it means no files were successfully fetched or all files failed the validation (hash check).
break
} }
const files: { [fileName: string]: SigitFile } = {} const files: { [fileName: string]: SigitFile } = {}
@ -481,10 +488,9 @@ export const VerifyPage = () => {
entryArrayBuffer, entryArrayBuffer,
entryFileName entryFileName
) )
const hash = await getHash(entryArrayBuffer)
if (hash) { fileHashes[entryFileName.replace(/^files\//, '')] =
fileHashes[entryFileName.replace(/^files\//, '')] = hash await getHash(entryArrayBuffer)
}
} else { } else {
fileHashes[entryFileName.replace(/^files\//, '')] = null fileHashes[entryFileName.replace(/^files\//, '')] = null
} }

View File

@ -9,14 +9,14 @@ import { RelaysDispatchTypes, RelaysState } from './relays/types'
import UserAppDataReducer from './userAppData/reducer' import UserAppDataReducer from './userAppData/reducer'
import { UserAppDataDispatchTypes } from './userAppData/types' import { UserAppDataDispatchTypes } from './userAppData/types'
import { UserDispatchTypes, UserState } from './user/types' import { UserDispatchTypes, UserState } from './user/types'
import { ServersDispatchTypes, ServersState } from './servers/types.ts' import { ServersDispatchTypes, FileServersState } from './servers/types.ts'
import serversReducer from './servers/reducer' import serversReducer from './servers/reducer'
export interface State { export interface State {
auth: AuthState auth: AuthState
user: UserState user: UserState
relays: RelaysState relays: RelaysState
servers: ServersState servers: FileServersState
userAppData?: UserAppData userAppData?: UserAppData
} }

View File

@ -1,7 +1,7 @@
import * as ActionTypes from '../actionTypes' import * as ActionTypes from '../actionTypes'
import { ServersDispatchTypes, ServersState } from './types' import { ServersDispatchTypes, FileServersState } from './types'
const initialState: ServersState = { const initialState: FileServersState = {
map: undefined, map: undefined,
mapUpdated: undefined mapUpdated: undefined
} }
@ -9,7 +9,7 @@ const initialState: ServersState = {
const reducer = ( const reducer = (
state = initialState, state = initialState,
action: ServersDispatchTypes action: ServersDispatchTypes
): ServersState => { ): FileServersState => {
switch (action.type) { switch (action.type) {
case ActionTypes.SET_SERVER_MAP: case ActionTypes.SET_SERVER_MAP:
return { ...state, map: action.payload, mapUpdated: Date.now() } return { ...state, map: action.payload, mapUpdated: Date.now() }

View File

@ -2,7 +2,7 @@ import * as ActionTypes from '../actionTypes'
import { RestoreState } from '../actions' import { RestoreState } from '../actions'
import { ServerMap } from '../../types' import { ServerMap } from '../../types'
export type ServersState = { export type FileServersState = {
map?: ServerMap map?: ServerMap
mapUpdated?: number mapUpdated?: number
} }

View File

@ -21,15 +21,14 @@ const getFileServerMap = async (
opts?: NDKSubscriptionOptions | undefined opts?: NDKSubscriptionOptions | undefined
) => Promise<NDKEvent | null> ) => Promise<NDKEvent | null>
): Promise<{ map: FileServerMap; mapUpdated?: number }> => { ): Promise<{ map: FileServerMap; mapUpdated?: number }> => {
try {
// More info about this kind of event available https://github.com/nostr-protocol/nips/blob/master/96.md // More info about this kind of event available https://github.com/nostr-protocol/nips/blob/master/96.md
const eventFilter: Filter = { const eventFilter: Filter = {
kinds: [kinds.FileServerPreference], kinds: [kinds.FileServerPreference],
authors: [npub] authors: [npub]
} }
const event = await fetchEvent(eventFilter).catch((err) => { const event = await fetchEvent(eventFilter)
return Promise.reject(err)
})
if (event) { if (event) {
// Handle found event 10096 // Handle found event 10096
@ -55,6 +54,9 @@ const getFileServerMap = async (
} else { } else {
return Promise.resolve({ map: getDefaultFileServerMap() }) return Promise.resolve({ map: getDefaultFileServerMap() })
} }
} catch (err) {
return Promise.reject(err)
}
} }
/** /**

View File

@ -1,6 +1,6 @@
import { NDKEvent, NDKUserProfile } from '@nostr-dev-kit/ndk' import { NDKEvent, NDKUserProfile } from '@nostr-dev-kit/ndk'
import { hexToBytes } from '@noble/hashes/utils' import { hexToBytes } from '@noble/hashes/utils'
import axios, { AxiosResponse } from 'axios' import axios from 'axios'
import { truncate } from 'lodash' import { truncate } from 'lodash'
import { import {
Event, Event,
@ -411,11 +411,10 @@ export const uploadUserAppDataToBlossom = async (
// Finalize the event with the private key // Finalize the event with the private key
const authEvent = finalizeEvent(event, hexToBytes(privateKey)) const authEvent = finalizeEvent(event, hexToBytes(privateKey))
const uploadPromises: Promise<AxiosResponse<FileServerPutResponse>>[] = []
// Upload the file to the file storage services using Axios // Upload the file to the file storage services using Axios
for (const preferredServer of preferredServers) { const responses = await Promise.all(
const uploadPromise = axios.put<FileServerPutResponse>( preferredServers.map((preferredServer) => {
return axios.put<FileServerPutResponse>(
`${preferredServer}/upload`, `${preferredServer}/upload`,
file, file,
{ {
@ -424,11 +423,8 @@ export const uploadUserAppDataToBlossom = async (
} }
} }
) )
})
uploadPromises.push(uploadPromise) )
}
const responses = await Promise.all(uploadPromises)
// Return the URLs of the uploaded files // Return the URLs of the uploaded files
return responses.map((response) => response.data.url) as string[] return responses.map((response) => response.data.url) as string[]
@ -520,189 +516,6 @@ export const getUserAppDataFromBlossom = async (
return parsedContent return parsedContent
} }
// /**
// * Function to subscribe to sigits notifications for a specified public key.
// * @param pubkey - The public key to subscribe to.
// * @returns A promise that resolves when the subscription is successful.
// */
// export const subscribeForSigits = async (pubkey: string) => {
// // Instantiate the MetadataController to retrieve relay list metadata
// const metadataController = MetadataController.getInstance()
// const relaySet = await metadataController
// .findRelayListMetadata(pubkey)
// .catch((err) => {
// // Log an error if retrieving relay list metadata fails
// console.log(
// `An error occurred while finding relay list metadata for ${hexToNpub(pubkey)}`,
// err
// )
// return null
// })
//
// // Return if metadata retrieval failed
// if (!relaySet) return
//
// // Ensure relay list is not empty
// if (relaySet.read.length === 0) return
//
// // Define the filter for the subscription
// const filter: Filter = {
// kinds: [1059],
// '#p': [pubkey]
// }
//
// // Process the received event synchronously
// const events = await relayController.fetchEvents(filter, relaySet.read)
// for (const e of events) {
// await processReceivedEvent(e)
// }
//
// // Async processing of the events has a race condition
// // relayController.subscribeForEvents(filter, relaySet.read, (event) => {
// // processReceivedEvent(event)
// // })
// }
// const processReceivedEvent = async (event: Event, difficulty: number = 5) => {
// const processedEvents = store.getState().userAppData?.processedGiftWraps
//
// // Abort processing if userAppData is undefined
// if (!processedEvents) return
//
// if (processedEvents.includes(event.id)) return
//
// store.dispatch(updateProcessedGiftWraps([...processedEvents, event.id]))
//
// // validate PoW
// // Count the number of leading zero bits in the hash
// const leadingZeroes = countLeadingZeroes(event.id)
// if (leadingZeroes < difficulty) return
//
// // decrypt the content of gift wrap event
// const nostrController = NostrController.getInstance()
// const decrypted = await nostrController.nip44Decrypt(
// event.pubkey,
// event.content
// )
//
// const internalUnsignedEvent = await parseJson<UnsignedEvent>(decrypted).catch(
// (err) => {
// console.log(
// 'An error occurred in parsing the internal unsigned event',
// err
// )
// return null
// }
// )
//
// if (!internalUnsignedEvent || internalUnsignedEvent.kind !== 938) return
//
// const parsedContent = await parseJson<Meta | SigitNotification>(
// internalUnsignedEvent.content
// ).catch((err) => {
// console.log('An error occurred in parsing the internal unsigned event', err)
// return null
// })
//
// if (!parsedContent) return
// let meta: Meta
// if (isSigitNotification(parsedContent)) {
// const notification = parsedContent
// let encryptionKey: string | undefined
// if (!notification.keys) return
//
// const { sender, keys } = notification.keys
//
// // Retrieve the user's public key from the state
// const usersPubkey = store.getState().auth.usersPubkey!
// const usersNpub = hexToNpub(usersPubkey)
//
// // Check if the user's public key is in the keys object
// if (usersNpub in keys) {
// // Instantiate the NostrController to decrypt the encryption key
// const nostrController = NostrController.getInstance()
// const decrypted = await nostrController
// .nip04Decrypt(sender, keys[usersNpub])
// .catch((err) => {
// console.log('An error occurred in decrypting encryption key', err)
// return undefined
// })
//
// encryptionKey = decrypted
// }
// try {
// meta = await fetchMetaFromFileStorage(
// notification.metaUrls,
// encryptionKey
// )
// } catch (error) {
// console.error(`An error occured fetching meta file from storage`, error)
// return
// }
// } else {
// meta = parsedContent
// }
//
// await updateUsersAppData(meta)
// }
// /**
// * Function to send a notification to a specified receiver.
// * @param receiver - The recipient's public key.
// * @param notification - Url pointing to metadata associated with the notification on blossom and keys to decrypt.
// */
// export const sendNotification = async (
// receiver: string,
// notification: SigitNotification
// ) => {
// // Retrieve the user's public key from the state
// const usersPubkey = store.getState().auth.usersPubkey!
//
// // Create an unsigned event object with the provided metadata
// const unsignedEvent: UnsignedEvent = {
// kind: 938,
// pubkey: usersPubkey,
// content: JSON.stringify(notification),
// tags: [],
// created_at: unixNow()
// }
//
// // Wrap the unsigned event with the receiver's information
// const wrappedEvent = createWrap(unsignedEvent, receiver)
//
// // Instantiate the MetadataController to retrieve relay list metadata
// const metadataController = MetadataController.getInstance()
// const relaySet = await metadataController
// .findRelayListMetadata(receiver)
// .catch((err) => {
// // Log an error if retrieving relay list metadata fails
// console.log(
// `An error occurred while finding relay list metadata for ${hexToNpub(receiver)}`,
// err
// )
// return null
// })
//
// // Return if metadata retrieval failed
// if (!relaySet) return
//
// // Ensure relay list is not empty
// if (relaySet.read.length === 0) return
//
// // Publish the notification event to the recipient's read relays
// await Promise.race([
// relayController.publish(wrappedEvent, relaySet.read),
// timeout(40 * 1000)
// ]).catch((err) => {
// // Log an error if publishing the notification event fails
// console.log(
// `An error occurred while publishing notification event for ${hexToNpub(receiver)}`,
// err
// )
// throw err
// })
// }
/** /**
* Show user's name, first available in order: display_name, name, or npub as fallback * Show user's name, first available in order: display_name, name, or npub as fallback
* @param npub User identifier, it can be either pubkey or npub1 (we only show npub) * @param npub User identifier, it can be either pubkey or npub1 (we only show npub)