feat: implement two-pane layout for /me page with article sources and highlights

This commit is contained in:
Gigi
2025-10-13 10:27:18 +02:00
parent 6c0a2439ad
commit 12393d6df4
4 changed files with 340 additions and 14 deletions

View File

@@ -1,4 +1,4 @@
import React, { useState, useEffect } from 'react'
import React, { useState, useEffect, useMemo } from 'react'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faSpinner, faExclamationCircle, faUser, faHighlighter } from '@fortawesome/free-solid-svg-icons'
import { Hooks } from 'applesauce-react'
@@ -8,6 +8,7 @@ import { Models } from 'applesauce-core'
import { Highlight } from '../types/highlights'
import { HighlightItem } from './HighlightItem'
import { fetchHighlights } from '../services/highlightService'
import ArticleSourceCard from './ArticleSourceCard'
interface MeProps {
relayPool: RelayPool
@@ -18,6 +19,7 @@ const Me: React.FC<MeProps> = ({ relayPool }) => {
const [highlights, setHighlights] = useState<Highlight[]>([])
const [loading, setLoading] = useState(true)
const [error, setError] = useState<string | null>(null)
const [selectedUrl, setSelectedUrl] = useState<string | null>(null)
const profile = useEventModel(Models.ProfileModel, activeAccount ? [activeAccount.pubkey] : null)
@@ -68,6 +70,36 @@ const Me: React.FC<MeProps> = ({ relayPool }) => {
setHighlights(prev => prev.filter(h => h.id !== highlightId))
}
// Group highlights by their URL reference
const groupedHighlights = useMemo(() => {
const grouped = new Map<string, Highlight[]>()
highlights.forEach(highlight => {
const url = highlight.urlReference || 'unknown'
if (!grouped.has(url)) {
grouped.set(url, [])
}
grouped.get(url)!.push(highlight)
})
// Sort by number of highlights (descending)
return Array.from(grouped.entries())
.sort((a, b) => b[1].length - a[1].length)
}, [highlights])
// Auto-select first article if nothing is selected
useEffect(() => {
if (!selectedUrl && groupedHighlights.length > 0) {
setSelectedUrl(groupedHighlights[0][0])
}
}, [groupedHighlights, selectedUrl])
// Get highlights for selected article
const selectedHighlights = useMemo(() => {
if (!selectedUrl) return []
return highlights.filter(h => (h.urlReference || 'unknown') === selectedUrl)
}, [highlights, selectedUrl])
if (loading) {
return (
<div className="explore-container">
@@ -90,25 +122,57 @@ const Me: React.FC<MeProps> = ({ relayPool }) => {
}
return (
<div className="explore-container">
<div className="explore-header">
<div className="me-container">
<div className="me-header">
<h1>
<FontAwesomeIcon icon={faUser} />
{getUserDisplayName()}
</h1>
<p className="explore-subtitle">
<FontAwesomeIcon icon={faHighlighter} /> {highlights.length} highlight{highlights.length !== 1 ? 's' : ''}
<p className="me-subtitle">
<FontAwesomeIcon icon={faHighlighter} /> {highlights.length} highlight{highlights.length !== 1 ? 's' : ''}
{' '}&bull;{' '} {groupedHighlights.length} source{groupedHighlights.length !== 1 ? 's' : ''}
</p>
</div>
<div className="highlights-list me-highlights-list">
{highlights.map((highlight) => (
<HighlightItem
key={highlight.id}
highlight={{ ...highlight, level: 'mine' }}
relayPool={relayPool}
onHighlightDelete={handleHighlightDelete}
/>
))}
<div className="me-two-pane">
<div className="me-sources-pane">
<h2 className="me-pane-title">Sources</h2>
<div className="me-sources-list">
{groupedHighlights.map(([url, hlts]) => (
<ArticleSourceCard
key={url}
url={url}
highlightCount={hlts.length}
isSelected={selectedUrl === url}
onClick={() => setSelectedUrl(url)}
/>
))}
</div>
</div>
<div className="me-highlights-pane">
<h2 className="me-pane-title">
Highlights
{selectedHighlights.length > 0 && (
<span className="me-pane-count"> ({selectedHighlights.length})</span>
)}
</h2>
<div className="me-highlights-list">
{selectedHighlights.length > 0 ? (
selectedHighlights.map((highlight) => (
<HighlightItem
key={highlight.id}
highlight={{ ...highlight, level: 'mine' }}
relayPool={relayPool}
onHighlightDelete={handleHighlightDelete}
/>
))
) : (
<div className="me-empty-state">
<FontAwesomeIcon icon={faHighlighter} size="2x" />
<p>Select a source to view highlights</p>
</div>
)}
</div>
</div>
</div>
</div>
)