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 = {
input: MarkInputSignature,
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) => {
// Value is the stringified signature object
// Encode it to the arrayBuffer
@ -43,8 +50,6 @@ export const SignatureStrategy: MarkStrategy = {
console.info(
`${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
} catch (error) {
if (error instanceof Error) {

View File

@ -26,6 +26,11 @@ import styles from '../UsersDetails.tsx/style.module.scss'
interface PdfMarkingProps {
currentUserMarks: CurrentUserMark[]
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
handleExport: () => void
handleEncryptedExport: () => void
@ -179,12 +184,10 @@ const PdfMarking = (props: PdfMarkingProps) => {
currentUserMarks={currentUserMarks}
otherUserMarks={otherUserMarks}
/>
{noFiles ? (
{noFiles && (
<Typography textAlign="center">
We were not able to retrieve the files.
</Typography>
) : (
''
)}
</StickySideColumns>

View File

@ -10,6 +10,11 @@ interface PdfViewProps {
currentFile: CurrentUserFile | null
currentUserMarks: CurrentUserMark[]
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
handleMarkClick: (id: number) => void
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([
ndkRelayListPromise,
serverMapPromise
getNDKRelayList(pubkey),
getFileServerMap(pubkey, fetchEvent)
])
const relays = ndkRelayList.relays

View File

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

View File

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

View File

@ -63,6 +63,11 @@ import { MarkRender } from '../../components/MarkTypeStrategy/MarkRender.tsx'
interface PdfViewProps {
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
currentFile: CurrentUserFile | null
parsedSignatureEvents: {
@ -460,7 +465,9 @@ export const VerifyPage = () => {
if (!zip) {
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 } = {}
@ -481,10 +488,9 @@ export const VerifyPage = () => {
entryArrayBuffer,
entryFileName
)
const hash = await getHash(entryArrayBuffer)
if (hash) {
fileHashes[entryFileName.replace(/^files\//, '')] = hash
}
fileHashes[entryFileName.replace(/^files\//, '')] =
await getHash(entryArrayBuffer)
} else {
fileHashes[entryFileName.replace(/^files\//, '')] = null
}

View File

@ -9,14 +9,14 @@ import { RelaysDispatchTypes, RelaysState } from './relays/types'
import UserAppDataReducer from './userAppData/reducer'
import { UserAppDataDispatchTypes } from './userAppData/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'
export interface State {
auth: AuthState
user: UserState
relays: RelaysState
servers: ServersState
servers: FileServersState
userAppData?: UserAppData
}

View File

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

View File

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

View File

@ -21,39 +21,41 @@ const getFileServerMap = async (
opts?: NDKSubscriptionOptions | undefined
) => Promise<NDKEvent | null>
): Promise<{ map: FileServerMap; mapUpdated?: number }> => {
// More info about this kind of event available https://github.com/nostr-protocol/nips/blob/master/96.md
const eventFilter: Filter = {
kinds: [kinds.FileServerPreference],
authors: [npub]
}
try {
// More info about this kind of event available https://github.com/nostr-protocol/nips/blob/master/96.md
const eventFilter: Filter = {
kinds: [kinds.FileServerPreference],
authors: [npub]
}
const event = await fetchEvent(eventFilter).catch((err) => {
const event = await fetchEvent(eventFilter)
if (event) {
// Handle found event 10096
const fileServersMap: FileServerMap = {}
const serverTags = event.tags.filter((tag) => tag[0] === 'server')
serverTags.forEach((tag) => {
const url = tag[1]
const serverType = tag[2]
// if 3rd element of server tag is undefined, server is WRITE and READ
fileServersMap[url] = {
write: serverType ? serverType === 'write' : true,
read: serverType ? serverType === 'read' : true
}
})
return Promise.resolve({
map: fileServersMap,
mapUpdated: event.created_at
})
} else {
return Promise.resolve({ map: getDefaultFileServerMap() })
}
} catch (err) {
return Promise.reject(err)
})
if (event) {
// Handle found event 10096
const fileServersMap: FileServerMap = {}
const serverTags = event.tags.filter((tag) => tag[0] === 'server')
serverTags.forEach((tag) => {
const url = tag[1]
const serverType = tag[2]
// if 3rd element of server tag is undefined, server is WRITE and READ
fileServersMap[url] = {
write: serverType ? serverType === 'write' : true,
read: serverType ? serverType === 'read' : true
}
})
return Promise.resolve({
map: fileServersMap,
mapUpdated: event.created_at
})
} else {
return Promise.resolve({ map: getDefaultFileServerMap() })
}
}

View File

@ -1,6 +1,6 @@
import { NDKEvent, NDKUserProfile } from '@nostr-dev-kit/ndk'
import { hexToBytes } from '@noble/hashes/utils'
import axios, { AxiosResponse } from 'axios'
import axios from 'axios'
import { truncate } from 'lodash'
import {
Event,
@ -411,24 +411,20 @@ export const uploadUserAppDataToBlossom = async (
// Finalize the event with the private key
const authEvent = finalizeEvent(event, hexToBytes(privateKey))
const uploadPromises: Promise<AxiosResponse<FileServerPutResponse>>[] = []
// Upload the file to the file storage services using Axios
for (const preferredServer of preferredServers) {
const uploadPromise = axios.put<FileServerPutResponse>(
`${preferredServer}/upload`,
file,
{
headers: {
Authorization: 'Nostr ' + btoa(JSON.stringify(authEvent)) // Set authorization header
const responses = await Promise.all(
preferredServers.map((preferredServer) => {
return axios.put<FileServerPutResponse>(
`${preferredServer}/upload`,
file,
{
headers: {
Authorization: 'Nostr ' + btoa(JSON.stringify(authEvent)) // Set authorization header
}
}
}
)
uploadPromises.push(uploadPromise)
}
const responses = await Promise.all(uploadPromises)
)
})
)
// Return the URLs of the uploaded files
return responses.map((response) => response.data.url) as string[]
@ -520,189 +516,6 @@ export const getUserAppDataFromBlossom = async (
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
* @param npub User identifier, it can be either pubkey or npub1 (we only show npub)