diff --git a/src/components/MarkTypeStrategy/Signature/index.tsx b/src/components/MarkTypeStrategy/Signature/index.tsx
index 57b19e6..90a441f 100644
--- a/src/components/MarkTypeStrategy/Signature/index.tsx
+++ b/src/components/MarkTypeStrategy/Signature/index.tsx
@@ -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) {
diff --git a/src/components/PDFView/PdfMarking.tsx b/src/components/PDFView/PdfMarking.tsx
index 71dfdd9..0f16db2 100644
--- a/src/components/PDFView/PdfMarking.tsx
+++ b/src/components/PDFView/PdfMarking.tsx
@@ -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 && (
We were not able to retrieve the files.
- ) : (
- ''
)}
diff --git a/src/components/PDFView/index.tsx b/src/components/PDFView/index.tsx
index 8fcaaeb..d8679ac 100644
--- a/src/components/PDFView/index.tsx
+++ b/src/components/PDFView/index.tsx
@@ -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[]
diff --git a/src/hooks/useAuth.ts b/src/hooks/useAuth.ts
index 39e5c5d..e2ef3ec 100644
--- a/src/hooks/useAuth.ts
+++ b/src/hooks/useAuth.ts
@@ -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
diff --git a/src/pages/settings/servers/index.tsx b/src/pages/settings/servers/index.tsx
index addfea5..79336b8 100644
--- a/src/pages/settings/servers/index.tsx
+++ b/src/pages/settings/servers/index.tsx
@@ -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)
})
})
}
diff --git a/src/pages/sign/index.tsx b/src/pages/sign/index.tsx
index d1bf242..d620b10 100644
--- a/src/pages/sign/index.tsx
+++ b/src/pages/sign/index.tsx
@@ -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!)
diff --git a/src/pages/verify/index.tsx b/src/pages/verify/index.tsx
index 2e16103..3c2afe1 100644
--- a/src/pages/verify/index.tsx
+++ b/src/pages/verify/index.tsx
@@ -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
}
diff --git a/src/store/rootReducer.ts b/src/store/rootReducer.ts
index 2de29a0..8daf1c8 100644
--- a/src/store/rootReducer.ts
+++ b/src/store/rootReducer.ts
@@ -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
}
diff --git a/src/store/servers/reducer.ts b/src/store/servers/reducer.ts
index 1f06a44..dc67acf 100644
--- a/src/store/servers/reducer.ts
+++ b/src/store/servers/reducer.ts
@@ -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() }
diff --git a/src/store/servers/types.ts b/src/store/servers/types.ts
index 41bda0a..be0baca 100644
--- a/src/store/servers/types.ts
+++ b/src/store/servers/types.ts
@@ -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
}
diff --git a/src/utils/file-servers.ts b/src/utils/file-servers.ts
index 108e14d..60c6af6 100644
--- a/src/utils/file-servers.ts
+++ b/src/utils/file-servers.ts
@@ -21,39 +21,41 @@ const getFileServerMap = async (
opts?: NDKSubscriptionOptions | undefined
) => Promise
): 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() })
}
}
diff --git a/src/utils/nostr.ts b/src/utils/nostr.ts
index 1915fe7..948d2f3 100644
--- a/src/utils/nostr.ts
+++ b/src/utils/nostr.ts
@@ -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>[] = []
-
// Upload the file to the file storage services using Axios
- for (const preferredServer of preferredServers) {
- const uploadPromise = axios.put(
- `${preferredServer}/upload`,
- file,
- {
- headers: {
- Authorization: 'Nostr ' + btoa(JSON.stringify(authEvent)) // Set authorization header
+ const responses = await Promise.all(
+ preferredServers.map((preferredServer) => {
+ return axios.put(
+ `${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(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(
-// 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)