+ >
+ )
+}
diff --git a/src/constants.ts b/src/constants.ts
index f0e7bff..16a3eef 100644
--- a/src/constants.ts
+++ b/src/constants.ts
@@ -36,3 +36,21 @@ export const LANDING_PAGE_DATA = {
}
]
}
+// we use this object to check if a user has reacted positively or negatively to a post
+// reactions are kind 7 events and their content is either emoji icon or emoji shortcode
+// Extend the following object as per need to include more emojis and shortcodes
+// NOTE: In following object emojis and shortcode array are not interlinked.
+// Both of these arrays can have separate items
+export const REACTIONS = {
+ positive: {
+ emojis: ['+', '❤️', '💙', '💖', '💚','⭐', '🚀', '🫂', '🎉', '🥳', '🎊', '👍', '💪', '😎'],
+ shortCodes: [':red_heart:', ':blue_heart:', ':sparkling_heart:', ':green_heart:', ':star:', ':rocket:', ':people_hugging:', ':party_popper:',
+ ':tada:', ':partying_face:', ':confetti_ball:', ':thumbs_up:', ':+1:', ':thumbsup:', ':thumbup:', ':flexed_biceps:', ':muscle:']
+ },
+ negative: {
+ emojis: ['-', '💩', '💔', '👎', '😠', '😞', '🤬', '🤢', '🤮', '🖕', '😡', '💢', '😠', '💀'],
+ shortCodes: [':poop:', ':shit:', ':poo:', ':hankey:', ':pile_of_poo:', ':broken_heart:', ':thumbsdown:', ':thumbdown:', ':nauseated_face:', ':sick:',
+ ':face_vomiting:', ':vomiting_face:', ':face_with_open_mouth_vomiting:', ':middle_finger:', ':rage:', ':anger:', ':anger_symbol:', ':angry_face:', ':angry:',
+ ':smiling_face_with_sunglasses:', ':sunglasses:', ':skull:', ':skeleton:']
+ }
+}
diff --git a/src/controllers/relay.ts b/src/controllers/relay.ts
index 2e362ca..75528c2 100644
--- a/src/controllers/relay.ts
+++ b/src/controllers/relay.ts
@@ -62,62 +62,77 @@ export class RelayController {
/**
* Publishes an event to multiple relays.
*
- * This method connects to the application relay and a set of write relays
- * obtained from the `MetadataController`. It then publishes the event to
- * all connected relays and returns a list of relays where the event was successfully published.
+ * This method establishes a connection to the application relay specified by
+ * an environment variable and a set of relays obtained from the
+ * `MetadataController`. It attempts to publish the event to all connected
+ * relays and returns a list of URLs of relays where the event was successfully
+ * published.
+ *
+ * If the process of finding relays or publishing the event takes too long,
+ * it handles the timeout to prevent blocking the operation.
*
* @param event - The event to be published.
- * @returns A promise that resolves to an array of URLs of relays where the event was published,
- * or an empty array if no relays were connected or the event could not be published.
+ * @param userHexKey - The user's hexadecimal public key, used to retrieve their relays.
+ * If not provided, the event's public key will be used.
+ * @param userRelaysType - The type of relays to be retrieved (e.g., write relays).
+ * Defaults to `UserRelaysType.Write`.
+ * @returns A promise that resolves to an array of URLs of relays where the event
+ * was published, or an empty array if no relays were connected or the
+ * event could not be published.
*/
- publish = async (event: Event): Promise => {
- // Connect to the application relay specified by environment variable
+ publish = async (
+ event: Event,
+ userHexKey?: string,
+ userRelaysType?: UserRelaysType
+ ): Promise => {
+ // Connect to the application relay specified by an environment variable
const appRelayPromise = this.connectRelay(import.meta.env.VITE_APP_RELAY)
- // todo: window.nostr.getRelays() is not implemented yet in nostr-login, implement the logic once its done
+ // TODO: Implement logic to retrieve relays using `window.nostr.getRelays()` once it becomes available in nostr-login.
+ // Retrieve an instance of MetadataController to find user relays
const metadataController = await MetadataController.getInstance()
- // Retrieve the list of write relays for the event's public key
- // Use a timeout to handle cases where retrieving write relays takes too long
- const writeRelaysPromise = metadataController.findUserRelays(
- event.pubkey,
- UserRelaysType.Write
+ // Retrieve the list of relays for the specified user's public key
+ // A timeout is used to prevent long waits if the relay retrieval is delayed
+ const relaysPromise = metadataController.findUserRelays(
+ userHexKey || event.pubkey,
+ userRelaysType || UserRelaysType.Write
)
log(this.debug, LogType.Info, `ℹ Finding user's write relays`)
- // Use Promise.race to either get the write relay URLs or timeout
- const writeRelayUrls = await Promise.race([
- writeRelaysPromise,
- timeout() // This is a custom timeout function that rejects the promise after a specified time
+ // Use Promise.race to either get the relay URLs or handle the timeout
+ const relayUrls = await Promise.race([
+ relaysPromise,
+ timeout() // Custom timeout function that rejects after a specified time
]).catch((err) => {
log(this.debug, LogType.Error, err)
return [] as string[] // Return an empty array if an error occurs
})
- // push admin relay urls obtained from metadata controller to writeRelayUrls list
+ // Add admin relay URLs from the metadata controller to the list of relay URLs
metadataController.adminRelays.forEach((url) => {
- writeRelayUrls.push(url)
+ relayUrls.push(url)
})
- // Connect to all write relays obtained from MetadataController
- const relayPromises = writeRelayUrls.map((relayUrl) =>
+ // Attempt to connect to all write relays obtained from MetadataController
+ const relayPromises = relayUrls.map((relayUrl) =>
this.connectRelay(relayUrl)
)
- // Wait for all relay connections to settle (either fulfilled or rejected)
+ // Wait for all relay connection attempts to settle (either fulfilled or rejected)
await Promise.allSettled([appRelayPromise, ...relayPromises])
- // Check if any relays are connected; if not, log an error and return null
+ // If no relays are connected, log an error and return an empty array
if (this.connectedRelays.length === 0) {
log(this.debug, LogType.Error, 'No relay is connected!')
return []
}
- const publishedOnRelays: string[] = [] // List to track which relays successfully published the event
+ const publishedOnRelays: string[] = [] // Track relays where the event was successfully published
- // Create a promise for publishing the event to each connected relay
+ // Create promises to publish the event to each connected relay
const publishPromises = this.connectedRelays.map((relay) => {
log(
this.debug,
@@ -128,7 +143,7 @@ export class RelayController {
return Promise.race([
relay.publish(event), // Publish the event to the relay
- timeout(30000) // Set a timeout to handle cases where publishing takes too long
+ timeout(30000) // Set a timeout to handle slow publishing operations
])
.then((res) => {
log(
@@ -137,7 +152,7 @@ export class RelayController {
`⬆️ nostr (${relay.url}): Publish result:`,
res
)
- publishedOnRelays.push(relay.url) // Add the relay URL to the list of successfully published relays
+ publishedOnRelays.push(relay.url) // Add successful relay URL to the list
})
.catch((err) => {
log(
@@ -153,16 +168,15 @@ export class RelayController {
await Promise.allSettled(publishPromises)
if (publishedOnRelays.length > 0) {
- // if the event was successfully published to relays then check if it contains the `aTag`
- // if so, then cache the event
-
+ // If the event was successfully published to any relays, check if it contains an `aTag`
+ // If the `aTag` is present, cache the event locally
const aTag = event.tags.find((item) => item[0] === 'a')
if (aTag && aTag[1]) {
this.events.set(aTag[1], event)
}
}
- // Return the list of relay URLs where the event was published
+ // Return the list of relay URLs where the event was successfully published
return publishedOnRelays
}
@@ -378,28 +392,19 @@ export class RelayController {
}
/**
- * Fetches an event from the user's relays based on a specified filter.
- * The function first retrieves the user's relays, and then fetches the event using the provided filter.
+ * Asynchronously retrieves multiple events from the user's relays based on a specified filter.
+ * The function first retrieves the user's relays, and then fetches the events using the provided filter.
*
* @param filter - The event filter to use when fetching the event (e.g., kinds, authors).
* @param hexKey - The hexadecimal representation of the user's public key.
* @param userRelaysType - The type of relays to search (e.g., write, read).
- * @returns A promise that resolves to the fetched event or null if the operation fails.
+ * @returns A promise that resolves with an array of events.
*/
- fetchEventFromUserRelays = async (
+ fetchEventsFromUserRelays = async (
filter: Filter,
hexKey: string,
userRelaysType: UserRelaysType
- ) => {
- // first check if event is present in cached map then return that
- // otherwise query relays
- if (filter['#a']) {
- const aTag = filter['#a'][0]
- const cachedEvent = this.events.get(aTag)
-
- if (cachedEvent) return cachedEvent
- }
-
+ ): Promise => {
// Get an instance of the MetadataController, which manages user metadata and relays
const metadataController = await MetadataController.getInstance()
@@ -423,7 +428,55 @@ export class RelayController {
})
// Fetch the event from the user's relays using the provided filter and relay URLs
- return this.fetchEvent(filter, relayUrls)
+ return this.fetchEvents(filter, relayUrls)
+ }
+
+ /**
+ * Fetches an event from the user's relays based on a specified filter.
+ * The function first retrieves the user's relays, and then fetches the event using the provided filter.
+ *
+ * @param filter - The event filter to use when fetching the event (e.g., kinds, authors).
+ * @param hexKey - The hexadecimal representation of the user's public key.
+ * @param userRelaysType - The type of relays to search (e.g., write, read).
+ * @returns A promise that resolves to the fetched event or null if the operation fails.
+ */
+ fetchEventFromUserRelays = async (
+ filter: Filter,
+ hexKey: string,
+ userRelaysType: UserRelaysType
+ ): Promise => {
+ // first check if event is present in cached map then return that
+ // otherwise query relays
+ if (filter['#a']) {
+ const aTag = filter['#a'][0]
+ const cachedEvent = this.events.get(aTag)
+
+ if (cachedEvent) return cachedEvent
+ }
+
+ const events = await this.fetchEventsFromUserRelays(
+ filter,
+ hexKey,
+ userRelaysType
+ )
+ // Sort events by creation date in descending order
+ events.sort((a, b) => b.created_at - a.created_at)
+
+ if (events.length > 0) {
+ const event = events[0]
+
+ // if the aTag was specified in filter then cache the fetched event before returning
+ if (filter['#a']) {
+ const aTag = filter['#a'][0]
+ this.events.set(aTag, event)
+ }
+
+ // return the event
+ return event
+ }
+
+ // return null if event array is empty
+ return null
}
getTotalZapAmount = async (
diff --git a/src/layout/header.tsx b/src/layout/header.tsx
index 8b1599b..27a157f 100644
--- a/src/layout/header.tsx
+++ b/src/layout/header.tsx
@@ -2,21 +2,18 @@ import {
init as initNostrLogin,
launch as launchNostrLoginDialog
} from 'nostr-login'
-import React, { useCallback, useEffect, useState } from 'react'
+import React, { useEffect, useState } from 'react'
import { Link } from 'react-router-dom'
-import { toast } from 'react-toastify'
import { Banner } from '../components/Banner'
-import { LoadingSpinner } from '../components/LoadingSpinner'
-import { ZapButtons, ZapPresets, ZapQR } from '../components/Zap'
-import { MetadataController, ZapController } from '../controllers'
-import { useAppDispatch, useAppSelector } from '../hooks'
+import { ZapPopUp } from '../components/Zap'
+import { MetadataController } from '../controllers'
+import { useAppDispatch, useAppSelector, useDidMount } from '../hooks'
import { appRoutes } from '../routes'
import { setAuth, setUser } from '../store/reducers/user'
import mainStyles from '../styles//main.module.scss'
import navStyles from '../styles/nav.module.scss'
import '../styles/popup.css'
-import { PaymentRequest } from '../types'
-import { formatNumber, npubToHex, unformatNumber } from '../utils'
+import { npubToHex } from '../utils'
export const Header = () => {
const dispatch = useAppDispatch()
@@ -173,7 +170,9 @@ export const Header = () => {