Merge pull request #59 from Permissionless-Software-Foundation/dh-channel-pfp

feat(nostr): Added channel PFPs
This commit is contained in:
Chris Troutner
2025-08-31 06:28:24 -07:00
committed by GitHub
6 changed files with 40 additions and 26 deletions
@@ -38,6 +38,7 @@ export default function ChannelItem (props) {
>
{chInfo?.name &&
<div className='d-flex align-items-center'>
{chInfo.picture && <img src={chInfo.picture} alt={chInfo.name} style={{ width: '30px', marginRight: '5px' }} />}
<span
className='fw-medium'
style={{
@@ -26,9 +26,13 @@ function ChatHeader (props) {
return (
<div className='p-3 d-flex justify-content-between align-items-center' style={{ backgroundColor: '#ffffff' }}>
{chInfo &&
<div className='flex-grow-1'>
<h5 className='mb-1 text-dark'>{chInfo?.name || getShortName(selectedChannel)}</h5>
<small className='text-muted'>{chInfo?.about || ''}</small>
<div className='d-flex justify-content-center align-items-center'>
{chInfo.picture && <img src={chInfo.picture} alt={chInfo.name} style={{ width: '50px', marginRight: '5px' }} />}
<div className='flex-grow-1'>
<h5 className='mb-1 text-dark'>{chInfo?.name || getShortName(selectedChannel)}</h5>
<small className='text-muted'>{chInfo?.about || ''}</small>
</div>
</div>}
{!chInfo &&
+12 -5
View File
@@ -19,6 +19,7 @@ function NostrChat (props) {
const [loadedMessages, setLoadedMessages] = useState(false)
const [profiles, setProfiles] = useState({})
const [channelsData, setChannelsData] = useState({})
const [channelsLoaded, setChannelsLoaded] = useState(false)
const [channels] = useState(config.chatsId)
const [selectedChannel, setSelectedChannel] = useState(config.chatsId[0])
@@ -50,10 +51,14 @@ function NostrChat (props) {
return
}
// Fetch profile.
let profile = await appData.nostrQueries.getProfile(pubKey)
if (!profile) profile = { name: pubKey }
console.log(`Trying to get ${pubKey} profile.`)
const defaultProfile = { name: pubKey } // 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
// Update profiles state
setProfiles(currentProfiles => {
const newProfiles = { ...currentProfiles }
@@ -68,7 +73,8 @@ function NostrChat (props) {
// Handle nostr pool
useEffect(() => {
if (!selectedChannel) return
// fetch messages when channel selected and channel metadata are loaded
if (!selectedChannel || !channelsLoaded) return
const relays = nostrQueries.relays
if (relays.length === 0) {
@@ -96,7 +102,7 @@ function NostrChat (props) {
console.log('Close existing pool')
pool.close()
}
}, [onMsgRead, selectedChannel, nostrQueries])
}, [onMsgRead, selectedChannel, nostrQueries, channelsLoaded])
// Load channels data
useEffect(() => {
@@ -120,6 +126,7 @@ function NostrChat (props) {
newChs[ch] = channelData
return newChs
})
setChannelsLoaded(true)
}
}
@@ -3,17 +3,16 @@
*/
// Global npm libraries
import React, { useEffect, useRef, useState } from 'react'
import React, { useEffect, useState } from 'react'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faUser } from '@fortawesome/free-solid-svg-icons'
import { Spinner } from 'react-bootstrap'
import NostrFormat from '../../app-body/nostr/nostr-format'
function MessageItem (props) {
const { message, profiles } = props
const [profile, setProfile] = useState(null)
const msgRef = useRef(null)
// Format timestamp
const formatTime = (timestamp) => {
return new Date(timestamp).toLocaleTimeString([], {
@@ -29,17 +28,8 @@ function MessageItem (props) {
}
}, [profiles, message, profile])
useEffect(() => {
if (msgRef.current && message.latest) {
msgRef.current.scrollIntoView({
behavior: 'smooth',
block: 'end'
})
}
}, [message])
return (
<div className='mb-3 d-flex align-items-start' ref={msgRef}>
<div className='mb-3 d-flex align-items-start'>
{/* Message Content */}
<div className='flex-grow-1'>
<div
@@ -81,7 +71,7 @@ function MessageItem (props) {
}}
/>}
</div>
{!profile && <span className='fw-bold text-dark me-2'>{message.pubkey}</span>}
{!profile && <span className='fw-bold text-dark me-2'>{message.pubkey}<Spinner animation='border' size='sm' /></span>}
{profile && profile.name && <span className='fw-bold text-dark me-2'>{profile.name}</span>}
<small className='text-muted'>{formatTime(message.created_at * 1000)}</small>
@@ -3,7 +3,7 @@
*/
// Global npm libraries
import React, { useEffect, useState, useCallback } from 'react'
import React, { useEffect, useState, useCallback, useRef } from 'react'
// Local libraries
import MessageItem from './message-item'
@@ -13,12 +13,11 @@ import { Spinner } from 'react-bootstrap'
function MessageList (props) {
const { messages, loadedMessages } = props
const [groupedMessages, setGroupedMessages] = useState([])
const msgContainerRef = useRef()
// Group messages by date
const groupMessagesByDate = useCallback((messages) => {
const grouped = {}
messages.forEach((message, i) => {
if (i === messages.length - 1) message.latest = true
const date = new Date(message.created_at * 1000).toDateString()
if (!grouped[date]) {
grouped[date] = []
@@ -33,6 +32,18 @@ function MessageList (props) {
const grouped = groupMessagesByDate(messages)
setGroupedMessages(grouped)
// Scroll to end of the message container.
// Wait few seconds before load render.
setTimeout(() => {
if (msgContainerRef.current) {
msgContainerRef.current.scrollTo({
top: msgContainerRef.current.scrollHeight - msgContainerRef.current.clientHeight,
behavior: 'smooth'
})
msgContainerRef.current = null // scroll one time
}
}, 1000)
}, [messages, groupMessagesByDate])
return (
@@ -66,7 +77,7 @@ function MessageList (props) {
)}
{loadedMessages && messages && messages.length > 0 && (
<div className='p-3' style={{ overflowY: 'auto', maxHeight: '50vh' }}>
<div ref={msgContainerRef} className='p-3' style={{ overflowY: 'auto', maxHeight: '50vh' }}>
{Object.entries(groupedMessages).map(([date, dateMessages]) => (
<div key={date}>
<DateSeparator date={date} />
+1
View File
@@ -54,6 +54,7 @@ export default class NostrQueries {
relay.close()
})
pool.on('error', (relay) => {
console.log(`Error fetching ${pubHex} profile. relay connection error :${relay.url} `)
relay.close()
resolve(false)
})