diff --git a/src/App.css b/src/App.css index e5a4c6d..0303ce3 100644 --- a/src/App.css +++ b/src/App.css @@ -41,6 +41,9 @@ width: auto!important; } +.cursor-pointer { + cursor: pointer; +} /* Remove input number arrows Chrome, Safari, Edge, Opera */ input::-webkit-outer-spin-button, diff --git a/src/components/app-body/nostr/global-feed/feed-card.js b/src/components/app-body/nostr/global-feed/feed-card.js new file mode 100644 index 0000000..1cf5c4b --- /dev/null +++ b/src/components/app-body/nostr/global-feed/feed-card.js @@ -0,0 +1,99 @@ +/** + * Component for reading global nostr posts + */ +// Global npm libraries +import React, { useCallback, useEffect, useState } from 'react' +import { Card, Spinner } from 'react-bootstrap' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import { faUser } from '@fortawesome/free-solid-svg-icons' +import * as nip19 from 'nostr-tools/nip19' + +function FeedCard (props) { + const { post, appData, profiles } = props + const [profile, setProfile] = useState(profiles[post.pubkey]) + + const [npub, setNpub] = useState('') + const [isClicked, setIsClicked] = useState(false) + + // Get npub from pubkey + useEffect(() => { + const npub = nip19.npubEncode(post.pubkey) + setNpub(npub) + }, [post]) + + // Verify if pubkey profile exists into the profiles state + useEffect(() => { + if (profiles[post.pubkey]) { + setProfile(profiles[post.pubkey]) + } + }, [profiles, post]) + + // copy to clipboard + const copyToClipboard = useCallback((value) => { + setIsClicked(true) + appData.appUtil.copyToClipboard(value) + setTimeout(() => setIsClicked(false), 200) + }, [appData]) + + // get short npub + const getShortNpub = useCallback((npub) => { + return npub.slice(0, 8) + '...' + npub.slice(-5) + }, []) + + return ( + + +
+
+ +
+
+
+ {profile && profile.name && {profile.name}} + {!profile?.name && ( + + {post.pubkey.slice(0, 8) + '...'} + + + )} +
+ {npub && ( + + copyToClipboard(npub)} + > + {getShortNpub(npub)} + + + )} +
+ + {new Date(post.created_at * 1000).toLocaleString()} + +
+
+
+ {post.content} +
+
+
+
+ + ) +} + +export default FeedCard diff --git a/src/components/app-body/nostr/global-feed/feed.js b/src/components/app-body/nostr/global-feed/feed.js index c216a3b..29ef4fd 100644 --- a/src/components/app-body/nostr/global-feed/feed.js +++ b/src/components/app-body/nostr/global-feed/feed.js @@ -2,18 +2,53 @@ * Component for reading global nostr posts */ // Global npm libraries -import React, { useEffect, useState } from 'react' -import { Container, Card, Spinner } from 'react-bootstrap' -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' -import { faUser } from '@fortawesome/free-solid-svg-icons' +import React, { useEffect, useState, useCallback } from 'react' +import { Container, Spinner } from 'react-bootstrap' import { RelayPool } from 'nostr' +import FeedCard from './feed-card' + function Feed (props) { - const { bchWalletState } = props.appData + const { appData } = props + const { bchWalletState } = appData const [posts, setPosts] = useState([]) const [loaded, setLoaded] = useState(false) + const [profiles, setProfiles] = useState({}) + // function to fetch profile and set it to profiles state + const fetchProfile = useCallback((pubkey) => { + // no fetch profile again if it exist + if (profiles[pubkey]) { + console.log('profile exists', profiles[pubkey]) + return profiles[pubkey] + } + + const psf = 'wss://nostr-relay.psfoundation.info' + + const pool = RelayPool([psf]) + pool.on('open', relay => { + relay.subscribe('subid', { limit: 5, kinds: [0], authors: [pubkey] }) + }) + + pool.on('eose', relay => { + console.log('Closing Relay') + relay.close() + }) + + pool.on('event', (relay, subId, ev) => { + try { + const profile = JSON.parse(ev.content) + const newProfiles = { ...profiles } + newProfiles[pubkey] = profile + setProfiles(newProfiles) + } catch (error) { + console.log('Error parsing profile', error) + } + }) + }, [profiles]) + + // Get global feed posts useEffect(() => { const start = () => { const psf = 'wss://nostr-relay.psfoundation.info' @@ -29,54 +64,25 @@ function Feed (props) { relay.close() }) - pool.on('event', (relay, subId, ev) => { + pool.on('event', async (relay, subId, ev) => { console.log('Received event:', ev) - // const profile = JSON.parse(ev.content) + setPosts(currentPosts => [...currentPosts, ev]) + // fetch post profile and set it to profiles state + fetchProfile(ev.pubkey) }) } if (!loaded) { start() } - }, [bchWalletState, loaded]) + }, [bchWalletState, loaded, fetchProfile]) return (
{posts.map((post, index) => ( - - -
-
- -
-
-
- {post.pubkey.slice(0, 8) + '...'} -
- - {`${post.pubkey.slice(0, 8)}...${post.pubkey.slice(-5)}`} - -
- - {new Date(post.created_at * 1000).toLocaleString()} - -
-
-
- {post.content} -
-
-
-
+ ))} {!posts.length && loaded && (