feat: add highlights filter button to reading progress filters

- Add 'highlighted' filter type to ReadingProgressFilterType
- New filter button with yellow highlighter icon
- Filter shows only articles that have highlights
- Highlights filter checks both eventReference and urlReference tags
- Color-coded: green for completed, yellow for highlighted, blue for others
- Applies to reads and links tabs in /me page
This commit is contained in:
Gigi
2025-10-19 22:15:13 +02:00
parent 5c82dff8df
commit de32310801
3 changed files with 38 additions and 9 deletions

View File

@@ -44,7 +44,7 @@ interface MeProps {
type TabType = 'highlights' | 'reading-list' | 'reads' | 'links' | 'writings'
// Valid reading progress filters
const VALID_FILTERS: ReadingProgressFilterType[] = ['all', 'unopened', 'started', 'reading', 'completed']
const VALID_FILTERS: ReadingProgressFilterType[] = ['all', 'unopened', 'started', 'reading', 'completed', 'highlighted']
const Me: React.FC<MeProps> = ({
relayPool,
@@ -481,8 +481,8 @@ const Me: React.FC<MeProps> = ({
const groups = groupIndividualBookmarks(filteredBookmarks)
// Apply reading progress filter
const filteredReads = filterByReadingProgress(reads, readingProgressFilter)
const filteredLinks = filterByReadingProgress(links, readingProgressFilter)
const filteredReads = filterByReadingProgress(reads, readingProgressFilter, highlights)
const filteredLinks = filterByReadingProgress(links, readingProgressFilter, highlights)
const sections: Array<{ key: string; title: string; items: IndividualBookmark[] }> =
groupingMode === 'flat'
? [{ key: 'all', title: `All Bookmarks (${filteredBookmarks.length})`, items: filteredBookmarks }]

View File

@@ -1,9 +1,9 @@
import React from 'react'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faBookOpen, faCheckCircle, faAsterisk } from '@fortawesome/free-solid-svg-icons'
import { faBookOpen, faCheckCircle, faAsterisk, faHighlighter } from '@fortawesome/free-solid-svg-icons'
import { faEnvelope, faEnvelopeOpen } from '@fortawesome/free-regular-svg-icons'
export type ReadingProgressFilterType = 'all' | 'unopened' | 'started' | 'reading' | 'completed'
export type ReadingProgressFilterType = 'all' | 'unopened' | 'started' | 'reading' | 'completed' | 'highlighted'
interface ReadingProgressFiltersProps {
selectedFilter: ReadingProgressFilterType
@@ -16,15 +16,23 @@ const ReadingProgressFilters: React.FC<ReadingProgressFiltersProps> = ({ selecte
{ type: 'unopened' as const, icon: faEnvelope, label: 'Unopened' },
{ type: 'started' as const, icon: faEnvelopeOpen, label: 'Started' },
{ type: 'reading' as const, icon: faBookOpen, label: 'Reading' },
{ type: 'completed' as const, icon: faCheckCircle, label: 'Completed' }
{ type: 'completed' as const, icon: faCheckCircle, label: 'Completed' },
{ type: 'highlighted' as const, icon: faHighlighter, label: 'Highlighted' }
]
return (
<div className="bookmark-filters">
{filters.map(filter => {
const isActive = selectedFilter === filter.type
// Only "completed" gets green color, everything else uses default blue
const activeStyle = isActive && filter.type === 'completed' ? { color: '#10b981' } : undefined
// Only "completed" gets green color, "highlighted" gets yellow, everything else uses default blue
let activeStyle: Record<string, string> | undefined = undefined
if (isActive) {
if (filter.type === 'completed') {
activeStyle = { color: '#10b981' } // green
} else if (filter.type === 'highlighted') {
activeStyle = { color: '#fde047' } // yellow
}
}
return (
<button

View File

@@ -1,16 +1,35 @@
import { ReadItem } from '../services/readsService'
import { ReadingProgressFilterType } from '../components/ReadingProgressFilters'
import { Highlight } from '../types/highlights'
/**
* Filters ReadItems by reading progress
*/
export function filterByReadingProgress(
items: ReadItem[],
filter: ReadingProgressFilterType
filter: ReadingProgressFilterType,
highlights?: Highlight[]
): ReadItem[] {
// Build a map of article references to highlight count
const articleHighlightCount = new Map<string, number>()
if (highlights) {
highlights.forEach(h => {
if (h.eventReference) {
const count = articleHighlightCount.get(h.eventReference) || 0
articleHighlightCount.set(h.eventReference, count + 1)
}
if (h.urlReference) {
const count = articleHighlightCount.get(h.urlReference) || 0
articleHighlightCount.set(h.urlReference, count + 1)
}
})
}
return items.filter((item) => {
const progress = item.readingProgress || 0
const isMarked = item.markedAsRead || false
const hasHighlights = (articleHighlightCount.get(item.id) || 0) > 0 ||
(item.url && (articleHighlightCount.get(item.url) || 0) > 0)
switch (filter) {
case 'unopened':
@@ -21,6 +40,8 @@ export function filterByReadingProgress(
return progress > 0.10 && progress <= 0.94 && !isMarked
case 'completed':
return progress >= 0.95 || isMarked
case 'highlighted':
return hasHighlights
case 'all':
default:
return true