Squash merge nostrfeed-ui into main
This commit is contained in:
parent
5063a3e121
commit
cc5e0dbef6
10 changed files with 379 additions and 258 deletions
|
|
@ -31,7 +31,7 @@ export interface ContentFilter {
|
|||
}
|
||||
|
||||
export interface FeedConfig {
|
||||
feedType: 'announcements' | 'general' | 'mentions' | 'events' | 'all' | 'custom'
|
||||
feedType: 'all' | 'announcements' | 'rideshare' | 'custom'
|
||||
maxPosts?: number
|
||||
adminPubkeys?: string[]
|
||||
contentFilters?: ContentFilter[]
|
||||
|
|
@ -176,8 +176,8 @@ export class FeedService extends BaseService {
|
|||
filter.authors = config.adminPubkeys
|
||||
}
|
||||
break
|
||||
case 'general':
|
||||
// General posts - no specific author filtering
|
||||
case 'rideshare':
|
||||
// Rideshare posts handled via content filters
|
||||
break
|
||||
case 'all':
|
||||
default:
|
||||
|
|
@ -188,9 +188,20 @@ export class FeedService extends BaseService {
|
|||
filters.push(filter)
|
||||
}
|
||||
|
||||
// Add reactions (kind 7) to the filters
|
||||
filters.push({
|
||||
kinds: [7], // Reactions
|
||||
limit: 500
|
||||
})
|
||||
|
||||
// Add ALL deletion events (kind 5) - we'll route them based on the 'k' tag
|
||||
filters.push({
|
||||
kinds: [5] // All deletion events (for both posts and reactions)
|
||||
})
|
||||
|
||||
console.log(`Creating feed subscription for ${config.feedType} with filters:`, filters)
|
||||
|
||||
// Subscribe to events with deduplication
|
||||
// Subscribe to all events (posts, reactions, deletions) with deduplication
|
||||
const unsubscribe = this.relayHub.subscribe({
|
||||
id: subscriptionId,
|
||||
filters: filters,
|
||||
|
|
@ -232,7 +243,21 @@ export class FeedService extends BaseService {
|
|||
* Handle new event with robust deduplication
|
||||
*/
|
||||
private handleNewEvent(event: NostrEvent, config: FeedConfig): void {
|
||||
// Skip if event already seen
|
||||
// Route deletion events (kind 5) based on what's being deleted
|
||||
if (event.kind === 5) {
|
||||
this.handleDeletionEvent(event)
|
||||
return
|
||||
}
|
||||
|
||||
// Route reaction events (kind 7) to ReactionService
|
||||
if (event.kind === 7) {
|
||||
if (this.reactionService) {
|
||||
this.reactionService.handleReactionEvent(event)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Skip if event already seen (for posts only, kind 1)
|
||||
if (this.seenEventIds.has(event.id)) {
|
||||
return
|
||||
}
|
||||
|
|
@ -313,21 +338,62 @@ export class FeedService extends BaseService {
|
|||
}, 'nostr-feed')
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle deletion events (NIP-09)
|
||||
* Routes deletions to appropriate service based on the 'k' tag
|
||||
*/
|
||||
private handleDeletionEvent(event: NostrEvent): void {
|
||||
// Check the 'k' tag to determine what kind of event is being deleted
|
||||
const kTag = event.tags?.find((tag: string[]) => tag[0] === 'k')
|
||||
const deletedKind = kTag ? kTag[1] : null
|
||||
|
||||
// Route to ReactionService for reaction deletions (kind 7)
|
||||
if (deletedKind === '7') {
|
||||
if (this.reactionService) {
|
||||
this.reactionService.handleDeletionEvent(event)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Handle post deletions (kind 1) in FeedService
|
||||
if (deletedKind === '1' || !deletedKind) {
|
||||
// Extract event IDs to delete from 'e' tags
|
||||
const eventIdsToDelete = event.tags
|
||||
?.filter((tag: string[]) => tag[0] === 'e')
|
||||
.map((tag: string[]) => tag[1]) || []
|
||||
|
||||
if (eventIdsToDelete.length === 0) {
|
||||
return
|
||||
}
|
||||
|
||||
// Remove deleted posts from the feed
|
||||
this._posts.value = this._posts.value.filter(post => {
|
||||
// Only delete if the deletion request comes from the same author (NIP-09 validation)
|
||||
if (eventIdsToDelete.includes(post.id) && post.pubkey === event.pubkey) {
|
||||
// Also remove from seen events so it won't be re-added
|
||||
this.seenEventIds.delete(post.id)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if event should be included in feed
|
||||
*/
|
||||
private shouldIncludeEvent(event: NostrEvent, config: FeedConfig): boolean {
|
||||
// Never include reactions (kind 7) or deletions (kind 5) in the main feed
|
||||
// These should only be processed by the ReactionService
|
||||
if (event.kind === 7 || event.kind === 5) {
|
||||
// Never include reactions (kind 7) in the main feed
|
||||
// Reactions should only be processed by the ReactionService
|
||||
if (event.kind === 7) {
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
const isAdminPost = config.adminPubkeys?.includes(event.pubkey) || false
|
||||
|
||||
// For custom content filters, check if event matches any active filter
|
||||
if (config.feedType === 'custom' && config.contentFilters) {
|
||||
// For custom content filters or specific feed types with filters, check if event matches any active filter
|
||||
if ((config.feedType === 'custom' || config.feedType === 'rideshare') && config.contentFilters) {
|
||||
console.log('FeedService: Using custom filters, count:', config.contentFilters.length)
|
||||
const result = config.contentFilters.some(filter => {
|
||||
console.log('FeedService: Checking filter:', filter.id, 'kinds:', filter.kinds, 'filterByAuthor:', filter.filterByAuthor)
|
||||
|
|
@ -347,26 +413,34 @@ export class FeedService extends BaseService {
|
|||
if (isAdminPost) return false
|
||||
}
|
||||
|
||||
// Apply keyword filtering if specified
|
||||
if (filter.keywords && filter.keywords.length > 0) {
|
||||
const content = event.content.toLowerCase()
|
||||
const hasMatchingKeyword = filter.keywords.some(keyword =>
|
||||
content.includes(keyword.toLowerCase())
|
||||
)
|
||||
if (!hasMatchingKeyword) {
|
||||
console.log('FeedService: No matching keywords found')
|
||||
return false
|
||||
}
|
||||
}
|
||||
// Apply keyword and tag filtering (OR logic when both are specified)
|
||||
const hasKeywordFilter = filter.keywords && filter.keywords.length > 0
|
||||
const hasTagFilter = filter.tags && filter.tags.length > 0
|
||||
|
||||
// Apply tag filtering if specified (check if event has any matching tags)
|
||||
if (filter.tags && filter.tags.length > 0) {
|
||||
const eventTags = event.tags?.filter(tag => tag[0] === 't').map(tag => tag[1]) || []
|
||||
const hasMatchingTag = filter.tags.some(filterTag =>
|
||||
eventTags.includes(filterTag)
|
||||
)
|
||||
if (!hasMatchingTag) {
|
||||
console.log('FeedService: No matching tags found')
|
||||
if (hasKeywordFilter || hasTagFilter) {
|
||||
let keywordMatch = false
|
||||
let tagMatch = false
|
||||
|
||||
// Check keywords
|
||||
if (hasKeywordFilter) {
|
||||
const content = event.content.toLowerCase()
|
||||
keywordMatch = filter.keywords!.some(keyword =>
|
||||
content.includes(keyword.toLowerCase())
|
||||
)
|
||||
}
|
||||
|
||||
// Check tags
|
||||
if (hasTagFilter) {
|
||||
const eventTags = event.tags?.filter(tag => tag[0] === 't').map(tag => tag[1]) || []
|
||||
tagMatch = filter.tags!.some(filterTag =>
|
||||
eventTags.includes(filterTag)
|
||||
)
|
||||
}
|
||||
|
||||
// Must match at least one: keywords OR tags
|
||||
const hasMatch = (hasKeywordFilter && keywordMatch) || (hasTagFilter && tagMatch)
|
||||
if (!hasMatch) {
|
||||
console.log('FeedService: No matching keywords or tags found')
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
|
@ -378,18 +452,14 @@ export class FeedService extends BaseService {
|
|||
return result
|
||||
}
|
||||
|
||||
// Legacy feed type handling
|
||||
// Feed type handling
|
||||
switch (config.feedType) {
|
||||
case 'announcements':
|
||||
return isAdminPost
|
||||
case 'general':
|
||||
return !isAdminPost
|
||||
case 'events':
|
||||
// Events feed could show all posts for now, or implement event-specific filtering
|
||||
return true
|
||||
case 'mentions':
|
||||
// TODO: Implement mention detection if needed
|
||||
return true
|
||||
case 'rideshare':
|
||||
// Rideshare filtering handled via content filters above
|
||||
// If we reach here, contentFilters weren't provided - show nothing
|
||||
return false
|
||||
case 'all':
|
||||
default:
|
||||
return true
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue