diff --git a/src/components/app-body/nostr-chat/chat-main.js b/src/components/app-body/nostr-chat/chat-main.js
index 17399c9..606e94e 100644
--- a/src/components/app-body/nostr-chat/chat-main.js
+++ b/src/components/app-body/nostr-chat/chat-main.js
@@ -9,6 +9,7 @@ import React from 'react'
import ChatHeader from './chat-header'
import MessageList from './message-list'
import MessageInput from './message-input'
+import { Spinner } from 'react-bootstrap'
function ChatMain (props) {
const { selectedChannel, messages } = props
@@ -16,9 +17,9 @@ function ChatMain (props) {
return (
<>
{!selectedChannel && (
-
+
-
Select a channel to start chatting
+
)}
diff --git a/src/components/app-body/nostr-chat/dm-item.js b/src/components/app-body/nostr-chat/dm-item.js
index b99fb08..c81259c 100644
--- a/src/components/app-body/nostr-chat/dm-item.js
+++ b/src/components/app-body/nostr-chat/dm-item.js
@@ -6,6 +6,7 @@
import React, { useCallback, useEffect, useState } from 'react'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faUser } from '@fortawesome/free-solid-svg-icons'
+import { Spinner } from 'react-bootstrap'
export default function DMItem (props) {
const { dm, profiles, selectedChannel, onChangeChannel } = props
@@ -97,6 +98,7 @@ export default function DMItem (props) {
style={{ fontSize: '14px' }}
>
{getShortName(profile?.name) || 'Unknown User'}
+ {!profile?.name &&
}
{/* 0 ? 'fw-bold' : ''}`}
diff --git a/src/components/app-body/nostr-chat/index.js b/src/components/app-body/nostr-chat/index.js
index 46025e6..095736e 100644
--- a/src/components/app-body/nostr-chat/index.js
+++ b/src/components/app-body/nostr-chat/index.js
@@ -14,7 +14,8 @@ import config from '../../../config'
function NostrChat (props) {
const { appData } = props
- const { nostrQueries, bchWalletState } = appData
+ const { nostrQueries, bchWalletState, startChannelChat } = appData
+
const [messages, setMessages] = useState([])
const [loadedMessages, setLoadedMessages] = useState(false)
const [profiles, setProfiles] = useState({})
@@ -24,12 +25,86 @@ function NostrChat (props) {
const [dmChannels, setDmChannels] = useState([])
const [dmListLoaded, setDmListLoaded] = useState(false)
- const [selectedChannel, setSelectedChannel] = useState(config.chatsId[0])
+ const [selectedChannel, setSelectedChannel] = useState(null)
const [selectedChannelIsDm, setSelectedChannelIsDm] = useState(false)
const profilesRef = useRef({})
const dmChannelsRef = useRef([])
+ // Reset states on change channel
+ const onChangeChannel = useCallback((ch) => {
+ if (selectedChannel === ch) return
+ const profiles = profilesRef.current
+ setSelectedChannelIsDm(!!profiles[ch])
+ setMessages([])
+ setLoadedMessages(false)
+ setSelectedChannel(ch)
+ }, [selectedChannel])
+
+ // Add a new DM to the list
+ const addPrivateMessage = useCallback(async (profile) => {
+ try {
+ const exist = dmChannelsRef.current.find(val => val === profile.pubKey)
+ setMessages([])
+ setLoadedMessages(false)
+ onChangeChannel(profile.pubKey)
+ if (exist) return
+ setDmChannels(currentChs => {
+ let newChs = [...currentChs]
+ newChs.push(profile.pubKey)
+ newChs = newChs.filter((val, i, list) => {
+ const existingIndex = list.findIndex(value => value === val)
+ return existingIndex === i
+ })
+ dmChannelsRef.current = newChs
+ return newChs
+ })
+ setChannelsData(currentChs => {
+ const newChs = { ...currentChs }
+ newChs[profile.pubKey] = profile
+ return newChs
+ })
+ } catch (error) {
+ console.warn(error)
+ }
+ }, [onChangeChannel])
+
+ // Define starter chat
+ useEffect(() => {
+ // Start dm if a initial chat is provided from props
+ const startDM = async (pubKey) => {
+ const npub = await appData.nostrQueries.hexToNpub(pubKey)
+
+ const defaultProfile = { name: npub } // default profile
+ profilesRef.current[pubKey] = defaultProfile // update ref , to prevent fetch this profile again.
+ // Fetch profile
+ const nostrProfile = await appData.nostrQueries.getProfile(pubKey)
+
+ const profile = nostrProfile || defaultProfile
+ // add public key formats to profile object
+ profile.pubKey = pubKey
+ profile.npub = npub
+ // Update profiles state
+ setProfiles(currentProfiles => {
+ const newProfiles = { ...currentProfiles }
+ newProfiles[pubKey] = profile
+ profilesRef.current = newProfiles
+ return newProfiles
+ })
+ await addPrivateMessage(profile)
+ }
+
+ if (selectedChannel) return
+ if (!startChannelChat) {
+ // Set group chat as initial chat
+ setSelectedChannel(config.chatsId[0])
+ setSelectedChannelIsDm(false)
+ } else if (dmListLoaded) {
+ // Set provided profile as initial chat
+ startDM(startChannelChat)
+ }
+ }, [appData, startChannelChat, dmListLoaded, addPrivateMessage, selectedChannel])
+
// Handle read messages
const onMsgRead = useCallback(async (ev) => {
try {
@@ -127,7 +202,7 @@ function NostrChat (props) {
})
pool.on('event', (relay, subId, ev) => {
- console.log('post retrieved from ', relay.url, ev.content)
+ console.log('Group post retrieved from ', relay.url, ev.content)
onMsgRead(ev)
})
@@ -141,13 +216,11 @@ function NostrChat (props) {
// Handle nostr pool for dm channels
useEffect(() => {
// fetch messages when channel selected and channel metadata are loaded
- console.log('selectedChannel', selectedChannel)
- console.log('selectedChannelIsDm', selectedChannelIsDm)
if (!selectedChannel || !selectedChannelIsDm) return
+
const { nostrKeyPair } = bchWalletState
const dmPubKey = selectedChannel
- console.log('dmPubKey', selectedChannel)
// Load messages for group channel
const relays = nostrQueries.relays
@@ -172,7 +245,7 @@ function NostrChat (props) {
})
pool.on('event', (relay, subId, ev) => {
- console.log('post retrieved from ', relay.url, ev.content)
+ console.log('DM post retrieved from ', relay.url, ev.content)
// decrpt message
if (ev.pubkey === nostrKeyPair.pubHex) {
// Sent messages
@@ -211,6 +284,7 @@ function NostrChat (props) {
setDmChannels(currentChs => {
const newChs = [...currentChs]
newChs.push(profile.pubKey)
+
dmChannelsRef.current = newChs
return newChs
})
@@ -254,18 +328,30 @@ function NostrChat (props) {
// Load Dm channels
useEffect(() => {
const loadCurrentDms = async () => {
+ const { nostrQueries, bchWalletState } = appData
const { nostrKeyPair } = bchWalletState
const dms = await appData.nostrQueries.getDms(nostrKeyPair.pubHex)
+ setDmChannels(currentChs => {
+ let newChs = [...currentChs, ...dms]
+ newChs = newChs.filter((val, i, list) => {
+ const existingIndex = list.findIndex(value => value === val)
+ return existingIndex === i
+ })
+ dmChannelsRef.current = newChs
+ return newChs
+ })
+ setDmListLoaded(true)
+
console.log('dm list', dms)
for (let i = 0; i < dms.length; i++) {
const pubKey = dms[i]
- const npub = await appData.nostrQueries.hexToNpub(pubKey)
+ const npub = await nostrQueries.hexToNpub(pubKey)
const defaultProfile = { name: npub } // default profile
profilesRef.current[pubKey] = defaultProfile // update ref , to prevent fetch this profile again.
// Fetch profile
- const nostrProfile = await appData.nostrQueries.getProfile(pubKey)
+ const nostrProfile = await nostrQueries.getProfile(pubKey)
const profile = nostrProfile || defaultProfile
// add public key formats to profile object
@@ -279,13 +365,10 @@ function NostrChat (props) {
return newProfiles
})
}
- setDmChannels(dms)
- setDmListLoaded(true)
- dmChannelsRef.current = dms
}
- loadCurrentDms()
- }, [bchWalletState, appData.nostrQueries])
+ if (!dmListLoaded) loadCurrentDms()
+ }, [appData, dmListLoaded])
// Load public channels data
useEffect(() => {
@@ -313,42 +396,9 @@ function NostrChat (props) {
}
}
- // loadMessages()
loadChData()
}, [selectedChannel, appData, groupChannels])
- const addPrivateMessage = async (profile) => {
- try {
- const exist = dmChannels.find(val => val === profile.pubKey)
- setMessages([])
- setLoadedMessages(false)
- onChangeChannel(profile.pubKey)
- if (exist) return
- setDmChannels(currentChs => {
- const newChs = [...currentChs]
- newChs.push(profile.pubKey)
- dmChannelsRef.current = newChs
- return newChs
- })
- setChannelsData(currentChs => {
- const newChs = { ...currentChs }
- newChs[profile.pubKey] = profile
- return newChs
- })
- } catch (error) {
- console.warn(error)
- }
- }
-
- // Reset states on change channel
- const onChangeChannel = useCallback((ch) => {
- if (selectedChannel === ch) return
- setSelectedChannelIsDm(!!profiles[ch])
- setMessages([])
- setLoadedMessages(false)
- setSelectedChannel(ch)
- }, [selectedChannel, profiles])
-
return (
<>
diff --git a/src/components/app-body/nostr-chat/message-list.js b/src/components/app-body/nostr-chat/message-list.js
index 5884bfa..cef3650 100644
--- a/src/components/app-body/nostr-chat/message-list.js
+++ b/src/components/app-body/nostr-chat/message-list.js
@@ -12,6 +12,7 @@ import { Spinner } from 'react-bootstrap'
function MessageList (props) {
const { messages, loadedMessages } = props
+ console.log('loadedMessages', loadedMessages)
const [groupedMessages, setGroupedMessages] = useState([])
const msgContainerRef = useRef()
// Group messages by date
diff --git a/src/components/app-body/nostr/profile/profile-read.js b/src/components/app-body/nostr/profile/profile-read.js
index 5fd6d0c..65ee226 100644
--- a/src/components/app-body/nostr/profile/profile-read.js
+++ b/src/components/app-body/nostr/profile/profile-read.js
@@ -10,6 +10,7 @@ import CopyOnClick from '../../bch-wallet/copy-on-click.js'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faUser, faGlobe } from '@fortawesome/free-solid-svg-icons'
import * as nip19 from 'nostr-tools/nip19'
+import { useNavigate } from 'react-router-dom'
// Local libraries
import NostrFormat from '../nostr-format'
@@ -18,14 +19,17 @@ function ProfileRead (props) {
const { npub, appData } = props
const { onProfileRead } = props
const [profile, setProfile] = useState({})
+ const [pubKey, setPubKey] = useState({})
+
const [loaded, setLoaded] = useState(false)
const [imageError, setImageError] = useState({ picture: false, banner: false })
+ const navigate = useNavigate()
useEffect(() => {
const start = async () => {
const pubHexData = nip19.decode(npub)
const pubHex = pubHexData.data
-
+ setPubKey(pubHex)
const profile = await appData.nostrQueries.getProfile(pubHex)
if (profile) {
onProfileRead(profile)
@@ -48,6 +52,13 @@ function ProfileRead (props) {
setImageError(prev => ({ ...prev, [type]: false }))
}
+ const handleMessage = () => {
+ console.log('appData', appData)
+ const { setStartChannelChat } = appData
+ setStartChannelChat(pubKey)
+ navigate('/nostr-chat')
+ }
+
return (
{/* Banner Section */}
@@ -146,6 +157,8 @@ function ProfileRead (props) {
variant='primary'
className='px-3 px-md-4 py-2 rounded-pill fw-semibold'
style={{ minWidth: '120px' }}
+ onClick={handleMessage}
+ disabled={!loaded}
>
Message
diff --git a/src/hooks/state.js b/src/hooks/state.js
index 8792360..e534cfd 100644
--- a/src/hooks/state.js
+++ b/src/hooks/state.js
@@ -58,6 +58,9 @@ function useAppState () {
// Nostr queries service
const nostrQueriesRef = useRef(new NostrQueries({ relays: readRelays }))
+ // ProfileDM
+ const [startChannelChat, setStartChannelChat] = useState('')
+
// The wallet state makes this a true progressive web app (PWA). As
// balances, UTXOs, and tokens are retrieved, this state is updated.
// properties are enumerated here for the purpose of documentation.
@@ -205,8 +208,9 @@ function useAppState () {
updateRelaysData,
restoreRelaysData,
readRelays,
- writeRelays
-
+ writeRelays,
+ startChannelChat,
+ setStartChannelChat
}
}
diff --git a/src/services/nostr-queries.js b/src/services/nostr-queries.js
index 6a8ebfa..0b36508 100644
--- a/src/services/nostr-queries.js
+++ b/src/services/nostr-queries.js
@@ -401,7 +401,7 @@ export default class NostrQueries {
})
pool.on('event', (relay, subId, ev) => {
- console.log('post retrieved from ', relay.url, ev.sig)
+ // console.log('post retrieved from ', relay.url, ev.sig)
if (ev.pubkey === pubKey) {
const pk = ev.tags[0][1]
list = [...list, pk]