diff --git a/src/components/Library/LibraryPublicationGrid.tsx b/src/components/Library/LibraryPublicationGrid.tsx
index e3c67949..af8cb86e 100644
--- a/src/components/Library/LibraryPublicationGrid.tsx
+++ b/src/components/Library/LibraryPublicationGrid.tsx
@@ -5,7 +5,7 @@ import type { LibraryPublicationEntry } from '@/lib/library-publication-index'
import { isBooklistNip32Label } from '@/lib/nip32-label'
import { cn } from '@/lib/utils'
import { useScreenSize } from '@/providers/ScreenSizeProvider'
-import { BookOpen, Highlighter, MessageSquare, Tag } from 'lucide-react'
+import { BookOpen, Bookmark, Highlighter, MessageSquare, Pin, Tag } from 'lucide-react'
import { useTranslation } from 'react-i18next'
function LabelBadgeIcon({ name }: { name: string }) {
@@ -23,7 +23,9 @@ function EngagementBadges({ entry }: { entry: LibraryPublicationEntry }) {
otherLabels.length === 0 &&
!entry.hasLabel &&
!entry.hasComment &&
- !entry.hasHighlight
+ !entry.hasHighlight &&
+ !entry.hasBookmark &&
+ !entry.hasPin
) {
return null
}
@@ -83,6 +85,18 @@ function EngagementBadges({ entry }: { entry: LibraryPublicationEntry }) {
{t('Library badge highlight')}
)}
+ {entry.hasBookmark && (
+
+
+ {t('Library badge bookmark')}
+
+ )}
+ {entry.hasPin && (
+
+
+ {t('Library badge pin')}
+
+ )}
)
}
diff --git a/src/hooks/useLibraryPublications.ts b/src/hooks/useLibraryPublications.ts
index 09c70b35..4a9e2cc1 100644
--- a/src/hooks/useLibraryPublications.ts
+++ b/src/hooks/useLibraryPublications.ts
@@ -40,7 +40,13 @@ const EMPTY_ENGAGEMENT: PublicationEngagementMaps = {
myHighlightAddresses: new Set(),
myHighlightEventIds: new Set(),
commentAddresses: new Set(),
- highlightAddresses: new Set()
+ commentEventIds: new Set(),
+ highlightAddresses: new Set(),
+ highlightEventIds: new Set(),
+ bookmarkAddresses: new Set(),
+ bookmarkEventIds: new Set(),
+ pinAddresses: new Set(),
+ pinEventIds: new Set()
}
const EMPTY_BOOKLIST_TARGETS = { addresses: new Set(), eventIds: new Set() }
diff --git a/src/i18n/locales/de.ts b/src/i18n/locales/de.ts
index 5349e0db..c4c70a7b 100644
--- a/src/i18n/locales/de.ts
+++ b/src/i18n/locales/de.ts
@@ -1664,6 +1664,8 @@ export default {
'Library badge label': 'Label',
'Library badge comment': 'Kommentar',
'Library badge highlight': 'Markierung',
+ 'Library badge bookmark': 'Lesezeichen',
+ 'Library badge pin': 'Angepinnt',
'Publication version': 'v{{version}}',
'Publication sections_one': '{{count}} Abschnitt',
'Publication sections_other': '{{count}} Abschnitte',
diff --git a/src/i18n/locales/en.ts b/src/i18n/locales/en.ts
index 9abe6aea..11409506 100644
--- a/src/i18n/locales/en.ts
+++ b/src/i18n/locales/en.ts
@@ -1689,6 +1689,8 @@ export default {
'Library badge my booklist': 'On my booklist',
'Library badge comment': 'Comment',
'Library badge highlight': 'Highlight',
+ 'Library badge bookmark': 'Bookmark',
+ 'Library badge pin': 'Pin',
'Add to my booklist': 'Add to my booklist',
'Remove from my booklist': 'Remove from my booklist',
'Add to my booklist failed': 'Failed to add to booklist',
diff --git a/src/lib/library-publication-index.test.ts b/src/lib/library-publication-index.test.ts
index e0c7fdc7..dbe8329f 100644
--- a/src/lib/library-publication-index.test.ts
+++ b/src/lib/library-publication-index.test.ts
@@ -39,6 +39,65 @@ function indexEvent(d: string, aTags: string[], id = d.padEnd(64, '0').slice(0,
}
describe('library-publication-index', () => {
+ it('matches comments and highlights by root event id', () => {
+ const root = indexEvent('book', [`30041:${PK}:intro`])
+ const indexByAddress = buildIndexByAddress([root])
+ const comment: Event = {
+ id: '8'.repeat(64),
+ kind: ExtendedKind.COMMENT,
+ pubkey: 'f'.repeat(64),
+ created_at: 50,
+ content: 'nice book',
+ tags: [['e', root.id]],
+ sig: 'e'.repeat(128)
+ }
+ const highlight: Event = {
+ id: '9'.repeat(64),
+ kind: kinds.Highlights,
+ pubkey: 'f'.repeat(64),
+ created_at: 50,
+ content: 'quote',
+ tags: [['e', root.id]],
+ sig: 'e'.repeat(128)
+ }
+ const engagement = buildEngagementMapsFromEvents([], [comment], [highlight])
+ const engaged = filterEngagedPublications([root], indexByAddress, engagement)
+ expect(engaged).toHaveLength(1)
+ expect(engaged[0].hasComment).toBe(true)
+ expect(engaged[0].hasHighlight).toBe(true)
+ })
+
+ it('matches bookmark and pin lists from any author', () => {
+ const rootAddr = `30040:${PK}:book`
+ const root = indexEvent('book', [`30041:${PK}:intro`])
+ const indexByAddress = buildIndexByAddress([root])
+ const bookmarkList: Event = {
+ id: 'b'.repeat(64),
+ kind: kinds.BookmarkList,
+ pubkey: 'f'.repeat(64),
+ created_at: 100,
+ content: '',
+ tags: [['a', rootAddr]],
+ sig: 'd'.repeat(128)
+ }
+ const pinList: Event = {
+ id: 'p'.repeat(64),
+ kind: 10001,
+ pubkey: 'e'.repeat(64),
+ created_at: 100,
+ content: '',
+ tags: [['e', root.id]],
+ sig: 'd'.repeat(128)
+ }
+ const engagement = buildEngagementMapsFromEvents([], [], [], undefined, undefined, null, [
+ bookmarkList
+ ], [pinList])
+ const engaged = filterEngagedPublications([root], indexByAddress, engagement)
+ expect(engaged).toHaveLength(1)
+ expect(engaged[0].hasBookmark).toBe(true)
+ expect(engaged[0].hasPin).toBe(true)
+ })
+
it('matches engagement on nested 30041 addresses', () => {
const leafAddr = `30041:${PK}:chapter-1`
const childAddr = `30040:${PK}:part-1`
@@ -147,6 +206,8 @@ describe('library-publication-index', () => {
hasMyHighlight: false,
hasComment: false,
hasHighlight: false,
+ hasBookmark: false,
+ hasPin: false,
engagementCount: 1
}
]
@@ -358,6 +419,8 @@ describe('library-publication-index', () => {
hasMyHighlight: false,
hasComment: false,
hasHighlight: false,
+ hasBookmark: false,
+ hasPin: false,
engagementCount: 0
},
{
@@ -370,6 +433,8 @@ describe('library-publication-index', () => {
hasMyHighlight: false,
hasComment: false,
hasHighlight: false,
+ hasBookmark: false,
+ hasPin: false,
engagementCount: 0
},
{
@@ -382,6 +447,8 @@ describe('library-publication-index', () => {
hasMyHighlight: false,
hasComment: true,
hasHighlight: false,
+ hasBookmark: false,
+ hasPin: false,
engagementCount: 1
},
{
@@ -394,6 +461,8 @@ describe('library-publication-index', () => {
hasMyHighlight: false,
hasComment: false,
hasHighlight: false,
+ hasBookmark: false,
+ hasPin: false,
engagementCount: 0
}
]
@@ -479,6 +548,8 @@ describe('library-publication-index', () => {
hasMyHighlight: false,
hasComment: false,
hasHighlight: false,
+ hasBookmark: false,
+ hasPin: false,
engagementCount: 0
}
const filtered = filterLibraryPublicationsByUser([entry], viewerPk, {
diff --git a/src/lib/library-publication-index.ts b/src/lib/library-publication-index.ts
index 42ee7ea2..fb82c7dc 100644
--- a/src/lib/library-publication-index.ts
+++ b/src/lib/library-publication-index.ts
@@ -54,6 +54,8 @@ const ENGAGEMENT_FETCH_TIMEOUT_MS = 25_000
const LIBRARY_SEARCH_READING_CACHE_LIMIT = 200
export const LIBRARY_RELAY_SEARCH_LIMIT = 100
const LIBRARY_RELAY_SEARCH_TIMEOUT_MS = 28_000
+/** NIP-51 pin list (kind 10001). */
+const PIN_LIST_KIND = 10001
const QUERY_OPTS = {
globalTimeout: 18_000,
eoseTimeout: 3_000,
@@ -74,7 +76,13 @@ export type PublicationEngagementMaps = {
myHighlightAddresses: Set
myHighlightEventIds: Set
commentAddresses: Set
+ commentEventIds: Set
highlightAddresses: Set
+ highlightEventIds: Set
+ bookmarkAddresses: Set
+ bookmarkEventIds: Set
+ pinAddresses: Set
+ pinEventIds: Set
}
export type LibraryPublicationEntry = {
@@ -88,6 +96,8 @@ export type LibraryPublicationEntry = {
hasMyHighlight: boolean
hasComment: boolean
hasHighlight: boolean
+ hasBookmark: boolean
+ hasPin: boolean
engagementCount: number
}
@@ -120,9 +130,15 @@ function librarySearchFingerprint(context: LibrarySearchContext): string {
? engagement.labelAddresses.size +
engagement.labelEventIds.size +
engagement.commentAddresses.size +
+ engagement.commentEventIds.size +
engagement.highlightAddresses.size +
+ engagement.highlightEventIds.size +
engagement.booklistAddresses.size +
engagement.booklistEventIds.size +
+ engagement.bookmarkAddresses.size +
+ engagement.bookmarkEventIds.size +
+ engagement.pinAddresses.size +
+ engagement.pinEventIds.size +
engagement.myBooklistAddresses.size +
engagement.myBooklistEventIds.size +
engagement.myCommentAddresses.size +
@@ -344,7 +360,9 @@ export function buildEngagementMapsFromEvents(
highlights: Event[],
targetAddresses?: Set,
targetEventIds?: Set,
- viewerPubkey?: string | null
+ viewerPubkey?: string | null,
+ bookmarkLists: Event[] = [],
+ pinLists: Event[] = []
): PublicationEngagementMaps {
const labelAddresses = new Set()
const labelEventIds = new Set()
@@ -359,7 +377,13 @@ export function buildEngagementMapsFromEvents(
const myHighlightAddresses = new Set()
const myHighlightEventIds = new Set()
const commentAddresses = new Set()
+ const commentEventIds = new Set()
const highlightAddresses = new Set()
+ const highlightEventIds = new Set()
+ const bookmarkAddresses = new Set()
+ const bookmarkEventIds = new Set()
+ const pinAddresses = new Set()
+ const pinEventIds = new Set()
const addressMatches = (addr: string) => !targetAddresses || targetAddresses.has(addr)
const eventIdMatches = (id: string) => !targetEventIds || targetEventIds.has(id.toLowerCase())
@@ -407,8 +431,9 @@ export function buildEngagementMapsFromEvents(
commentAddresses.add(tag[1])
if (isViewerEvent) myCommentAddresses.add(tag[1])
}
- if (tag[0] === 'e' && tag[1] && eventIdMatches(tag[1]) && isViewerEvent) {
- myCommentEventIds.add(tag[1].toLowerCase())
+ if (tag[0] === 'e' && tag[1] && eventIdMatches(tag[1])) {
+ commentEventIds.add(tag[1].toLowerCase())
+ if (isViewerEvent) myCommentEventIds.add(tag[1].toLowerCase())
}
}
}
@@ -420,8 +445,31 @@ export function buildEngagementMapsFromEvents(
highlightAddresses.add(tag[1])
if (isViewerEvent) myHighlightAddresses.add(tag[1])
}
- if (tag[0] === 'e' && tag[1] && eventIdMatches(tag[1]) && isViewerEvent) {
- myHighlightEventIds.add(tag[1].toLowerCase())
+ if (tag[0] === 'e' && tag[1] && eventIdMatches(tag[1])) {
+ highlightEventIds.add(tag[1].toLowerCase())
+ if (isViewerEvent) myHighlightEventIds.add(tag[1].toLowerCase())
+ }
+ }
+ }
+
+ for (const ev of bookmarkLists) {
+ for (const tag of ev.tags) {
+ if (tag[0] === 'a' && tag[1] && addressMatches(tag[1])) {
+ bookmarkAddresses.add(tag[1])
+ }
+ if (tag[0] === 'e' && tag[1] && eventIdMatches(tag[1])) {
+ bookmarkEventIds.add(tag[1].toLowerCase())
+ }
+ }
+ }
+
+ for (const ev of pinLists) {
+ for (const tag of ev.tags) {
+ if (tag[0] === 'a' && tag[1] && addressMatches(tag[1])) {
+ pinAddresses.add(tag[1])
+ }
+ if (tag[0] === 'e' && tag[1] && eventIdMatches(tag[1])) {
+ pinEventIds.add(tag[1].toLowerCase())
}
}
}
@@ -440,7 +488,13 @@ export function buildEngagementMapsFromEvents(
myHighlightAddresses,
myHighlightEventIds,
commentAddresses,
- highlightAddresses
+ commentEventIds,
+ highlightAddresses,
+ highlightEventIds,
+ bookmarkAddresses,
+ bookmarkEventIds,
+ pinAddresses,
+ pinEventIds
}
}
@@ -500,13 +554,34 @@ export async function fetchPublicationEngagementMaps(
const commentWsFilters = addressChunks.map(
(chunk): Filter => ({ kinds: [ExtendedKind.COMMENT], '#A': chunk, limit: chunk.length * 12 })
)
+ const commentEventFilters = eventIdChunks.map(
+ (chunk): Filter => ({ kinds: [ExtendedKind.COMMENT], '#e': chunk, limit: chunk.length * 12 })
+ )
+ const highlightEventFilters = eventIdChunks.map(
+ (chunk): Filter => ({ kinds: [kinds.Highlights], '#e': chunk, limit: chunk.length * 12 })
+ )
+ const bookmarkAddressFilters = addressChunks.map(
+ (chunk): Filter => ({ kinds: [kinds.BookmarkList], '#a': chunk, limit: chunk.length * 8 })
+ )
+ const bookmarkEventFilters = eventIdChunks.map(
+ (chunk): Filter => ({ kinds: [kinds.BookmarkList], '#e': chunk, limit: chunk.length * 8 })
+ )
+ const pinAddressFilters = addressChunks.map(
+ (chunk): Filter => ({ kinds: [PIN_LIST_KIND], '#a': chunk, limit: chunk.length * 8 })
+ )
+ const pinEventFilters = eventIdChunks.map(
+ (chunk): Filter => ({ kinds: [PIN_LIST_KIND], '#e': chunk, limit: chunk.length * 8 })
+ )
const highlightPromise = Promise.all([
useWsEngagement && highlightFilters.length > 0
? queryService.fetchEvents(wsRelays, highlightFilters, QUERY_OPTS)
: Promise.resolve([] as Event[]),
+ useWsEngagement && highlightEventFilters.length > 0
+ ? queryService.fetchEvents(wsRelays, highlightEventFilters, QUERY_OPTS)
+ : Promise.resolve([] as Event[]),
fetchHttpEngagementByAddresses(httpRelays, kinds.Highlights, '#a', addressChunks)
- ]).then(([scoped, bulk]) => dedupeEventsById([...scoped, ...bulk]))
+ ]).then(([scoped, byEvent, bulk]) => dedupeEventsById([...scoped, ...byEvent, ...bulk]))
const labelPromise = Promise.all([
useWsEngagement && labelAddressFilters.length > 0
@@ -522,13 +597,38 @@ export async function fetchPublicationEngagementMaps(
useWsEngagement && commentWsFilters.length > 0
? queryService.fetchEvents(wsRelays, commentWsFilters, QUERY_OPTS)
: Promise.resolve([] as Event[]),
+ useWsEngagement && commentEventFilters.length > 0
+ ? queryService.fetchEvents(wsRelays, commentEventFilters, QUERY_OPTS)
+ : Promise.resolve([] as Event[]),
fetchHttpEngagementByAddresses(httpRelays, ExtendedKind.COMMENT, '#A', addressChunks)
- ]).then(([scoped, bulk]) => dedupeEventsById([...scoped, ...bulk]))
+ ]).then(([scoped, byEvent, bulk]) => dedupeEventsById([...scoped, ...byEvent, ...bulk]))
+
+ const bookmarkPromise = Promise.all([
+ useWsEngagement && bookmarkAddressFilters.length > 0
+ ? queryService.fetchEvents(wsRelays, bookmarkAddressFilters, QUERY_OPTS)
+ : Promise.resolve([] as Event[]),
+ useWsEngagement && bookmarkEventFilters.length > 0
+ ? queryService.fetchEvents(wsRelays, bookmarkEventFilters, QUERY_OPTS)
+ : Promise.resolve([] as Event[]),
+ fetchHttpEngagementByAddresses(httpRelays, kinds.BookmarkList, '#a', addressChunks)
+ ]).then(([byAddress, byEvent, bulk]) => dedupeEventsById([...byAddress, ...byEvent, ...bulk]))
- const [highlights, labels, comments] = await Promise.all([
+ const pinPromise = Promise.all([
+ useWsEngagement && pinAddressFilters.length > 0
+ ? queryService.fetchEvents(wsRelays, pinAddressFilters, QUERY_OPTS)
+ : Promise.resolve([] as Event[]),
+ useWsEngagement && pinEventFilters.length > 0
+ ? queryService.fetchEvents(wsRelays, pinEventFilters, QUERY_OPTS)
+ : Promise.resolve([] as Event[]),
+ fetchHttpEngagementByAddresses(httpRelays, PIN_LIST_KIND, '#a', addressChunks)
+ ]).then(([byAddress, byEvent, bulk]) => dedupeEventsById([...byAddress, ...byEvent, ...bulk]))
+
+ const [highlights, labels, comments, bookmarkLists, pinLists] = await Promise.all([
highlightPromise,
labelPromise,
- commentPromise
+ commentPromise,
+ bookmarkPromise,
+ pinPromise
])
return buildEngagementMapsFromEvents(
@@ -537,7 +637,9 @@ export async function fetchPublicationEngagementMaps(
dedupeEventsById(highlights),
targetAddresses,
targetEventIds,
- options?.viewerPubkey
+ options?.viewerPubkey,
+ dedupeEventsById(bookmarkLists),
+ dedupeEventsById(pinLists)
)
}
@@ -546,14 +648,56 @@ function addressHasEngagement(
eventId: string | undefined,
maps: PublicationEngagementMaps
): { hasLabel: boolean; hasComment: boolean; hasHighlight: boolean } {
+ const idLower = eventId?.toLowerCase()
const hasLabel =
- maps.labelAddresses.has(address) ||
- (eventId ? maps.labelEventIds.has(eventId.toLowerCase()) : false)
- const hasComment = maps.commentAddresses.has(address)
- const hasHighlight = maps.highlightAddresses.has(address)
+ maps.labelAddresses.has(address) || (idLower ? maps.labelEventIds.has(idLower) : false)
+ const hasComment =
+ maps.commentAddresses.has(address) || (idLower ? maps.commentEventIds.has(idLower) : false)
+ const hasHighlight =
+ maps.highlightAddresses.has(address) || (idLower ? maps.highlightEventIds.has(idLower) : false)
return { hasLabel, hasComment, hasHighlight }
}
+function collectBookmarkPinFlagsForTarget(
+ address: string,
+ eventId: string | undefined,
+ maps: PublicationEngagementMaps
+): { hasBookmark: boolean; hasPin: boolean } {
+ const idLower = eventId?.toLowerCase()
+ return {
+ hasBookmark:
+ maps.bookmarkAddresses.has(address) || (idLower ? maps.bookmarkEventIds.has(idLower) : false),
+ hasPin: maps.pinAddresses.has(address) || (idLower ? maps.pinEventIds.has(idLower) : false)
+ }
+}
+
+function targetHasPublicationEngagement(
+ flags: { hasLabel: boolean; hasComment: boolean; hasHighlight: boolean },
+ booklistFlags: { hasBooklistLabel: boolean },
+ bookmarkPinFlags: { hasBookmark: boolean; hasPin: boolean }
+): boolean {
+ return (
+ flags.hasLabel ||
+ flags.hasComment ||
+ flags.hasHighlight ||
+ booklistFlags.hasBooklistLabel ||
+ bookmarkPinFlags.hasBookmark ||
+ bookmarkPinFlags.hasPin
+ )
+}
+
+/** True when a library row has any engagement signal from any author. */
+export function publicationEntryHasEngagement(entry: LibraryPublicationEntry): boolean {
+ return (
+ entry.hasLabel ||
+ entry.hasBooklistLabel ||
+ entry.hasComment ||
+ entry.hasHighlight ||
+ entry.hasBookmark ||
+ entry.hasPin
+ )
+}
+
function collectLabelNamesForTarget(
address: string,
eventId: string | undefined,
@@ -617,6 +761,8 @@ export function buildLibraryPublicationEntry(
let hasLabel = false
let hasComment = false
let hasHighlight = false
+ let hasBookmark = false
+ let hasPin = false
let hasBooklistLabel = false
let hasMyBooklistLabel = false
let hasMyComment = false
@@ -628,6 +774,7 @@ export function buildLibraryPublicationEntry(
const indexed = indexByAddress.get(addr)
const flags = addressHasEngagement(addr, indexed?.id, engagement)
const booklistFlags = collectBooklistFlagsForTarget(addr, indexed?.id, engagement)
+ const bookmarkPinFlags = collectBookmarkPinFlagsForTarget(addr, indexed?.id, engagement)
const myFlags = collectMyEngagementFlagsForTarget(addr, indexed?.id, engagement)
if (flags.hasLabel) {
hasLabel = true
@@ -639,15 +786,20 @@ export function buildLibraryPublicationEntry(
if (myFlags.hasMyHighlight) hasMyHighlight = true
if (flags.hasComment) hasComment = true
if (flags.hasHighlight) hasHighlight = true
- if (flags.hasLabel || flags.hasComment || flags.hasHighlight) engagementCount++
+ if (bookmarkPinFlags.hasBookmark) hasBookmark = true
+ if (bookmarkPinFlags.hasPin) hasPin = true
+ if (targetHasPublicationEngagement(flags, booklistFlags, bookmarkPinFlags)) engagementCount++
}
const rootFlags = addressHasEngagement(rootAddr ?? '', root.id, engagement)
const rootBooklistFlags = collectBooklistFlagsForTarget(rootAddr ?? '', root.id, engagement)
+ const rootBookmarkPinFlags = collectBookmarkPinFlagsForTarget(rootAddr ?? '', root.id, engagement)
const rootMyFlags = collectMyEngagementFlagsForTarget(rootAddr ?? '', root.id, engagement)
hasLabel = hasLabel || rootFlags.hasLabel
hasComment = hasComment || rootFlags.hasComment
hasHighlight = hasHighlight || rootFlags.hasHighlight
+ hasBookmark = hasBookmark || rootBookmarkPinFlags.hasBookmark
+ hasPin = hasPin || rootBookmarkPinFlags.hasPin
hasBooklistLabel = hasBooklistLabel || rootBooklistFlags.hasBooklistLabel
hasMyBooklistLabel = hasMyBooklistLabel || rootBooklistFlags.hasMyBooklistLabel
hasMyComment = hasMyComment || rootMyFlags.hasMyComment
@@ -666,6 +818,8 @@ export function buildLibraryPublicationEntry(
hasMyHighlight,
hasComment,
hasHighlight,
+ hasBookmark,
+ hasPin,
engagementCount
}
}
@@ -687,7 +841,7 @@ export function filterEngagedPublications(
): LibraryPublicationEntry[] {
return getTopLevelIndexEvents(roots)
.map((root) => buildLibraryPublicationEntry(root, indexByAddress, engagement))
- .filter((entry) => entry.hasLabel || entry.hasComment || entry.hasHighlight)
+ .filter((entry) => publicationEntryHasEngagement(entry))
}
export function buildRecentPublicationEntries(
@@ -713,7 +867,7 @@ export function computeLibraryFeedRootOrder(
const restRoots: Event[] = []
for (const root of topLevel) {
const entry = buildLibraryPublicationEntry(root, indexByAddress, engagement)
- if (entry.hasLabel || entry.hasComment || entry.hasHighlight) {
+ if (publicationEntryHasEngagement(entry)) {
engagedRoots.push(root)
} else {
restRoots.push(root)
@@ -787,7 +941,9 @@ export function pickLibraryPublicationEntries(
export function sortLibraryPublications(entries: LibraryPublicationEntry[]): LibraryPublicationEntry[] {
return [...entries].sort((a, b) => {
- if (a.hasLabel !== b.hasLabel) return a.hasLabel ? -1 : 1
+ const aEngaged = publicationEntryHasEngagement(a)
+ const bEngaged = publicationEntryHasEngagement(b)
+ if (aEngaged !== bEngaged) return aEngaged ? -1 : 1
if (a.engagementCount !== b.engagementCount) return b.engagementCount - a.engagementCount
return b.event.created_at - a.event.created_at
})
@@ -810,7 +966,13 @@ function emptyPublicationEngagementMaps(): PublicationEngagementMaps {
myHighlightAddresses: new Set(),
myHighlightEventIds: new Set(),
commentAddresses: new Set(),
- highlightAddresses: new Set()
+ commentEventIds: new Set(),
+ highlightAddresses: new Set(),
+ highlightEventIds: new Set(),
+ bookmarkAddresses: new Set(),
+ bookmarkEventIds: new Set(),
+ pinAddresses: new Set(),
+ pinEventIds: new Set()
}
}