* Hide reactions, reposts, bots from feed

* Improve feed performance
This commit is contained in:
styppo 2023-01-17 00:13:15 +00:00
parent ecbb2f98e3
commit 2e78bdd51e
No known key found for this signature in database
GPG Key ID: 3AAA685C50724C28
5 changed files with 87 additions and 37 deletions

View File

@ -44,7 +44,6 @@ export default defineComponent({
methods: {
async load() {
if (this.loading) return
console.log('loading')
this.loading = true
this.$emit('loading')

View File

@ -41,21 +41,28 @@ export default class Note {
return this.eventRefs().ancestor()
}
pubkeyTags() {
return this.tags.filter(tag => tag.type === TagType.PUBKEY)
}
eventTags() {
return this.tags.filter(tag => tag.type === TagType.EVENT)
}
pubkeyRefs() {
return this.tags
.filter(tag => tag.type === TagType.PUBKEY)
.map(tag => tag.ref)
return this.pubkeyTags().map(tag => tag.ref)
}
eventRefs() {
const refs = this.tags
.filter(tag => tag.type === TagType.EVENT)
.map(tag => tag.ref)
return new EventRefs(refs)
return new EventRefs(this.eventTags().map(tag => tag.ref))
}
relatedPubkeys() {
return [this.author].concat(this.pubkeyRefs())
}
contentTagRefs() {
const regex = /#\[([0-9]+)]/ig
const regex = /#\[([0-9]+)]/g
let refs = []
let match
while ((match = regex.exec(this.content))) {
@ -64,9 +71,16 @@ export default class Note {
return refs
}
isRepostOrTag() {
return Note.isRepostOrTag(this)
}
static isRepostOrTag(event) {
return /#\[([0-9]+)]/.test(event.content)
}
isReaction() {
return this.kind === EventKind.REACTION
|| (this.hasAncestor() && Note.isReactionContent(this.content))
return Note.isReaction(this)
}
static isReaction(event) {

View File

@ -22,6 +22,9 @@ export const useStatStore = defineStore('stat', {
if (Note.isReaction(event)) {
const stats = this.getOrInit(event.eventRefs().ancestor())
stats.reactions++
} else if (Note.isRepostOrTag(event)) {
const stats = this.getOrInit(event.eventRefs().ancestor())
stats.shares++
} else {
for (const eventId of event.eventRefs()) {
const stats = this.getOrInit(eventId)

View File

@ -33,13 +33,13 @@
<div class="load-more-container" :class="{'more-available': numUnreads}">
<AsyncLoadButton
v-if="numUnreads"
:load-fn="loadUnreads"
:load-fn="loadNewer"
:label="`Load ${numUnreads} unreads`"
/>
</div>
<template v-for="(thread, i) in feedItems">
<Thread v-if="defer(i)" :key="thread[0].id" :thread="thread" class="full-width" />
<Thread v-if="true || defer(i)" :key="thread[0].id" :thread="thread" class="full-width" />
</template>
<ListPlaceholder :count="feedItems?.length" :loading="loading" />
@ -66,6 +66,7 @@ import {useNostrStore} from 'src/nostr/NostrStore'
import Defer from 'src/utils/Defer'
import {EventKind} from 'src/nostr/model/Event'
import DateUtils from 'src/utils/DateUtils'
import Bots from 'src/utils/bots'
const Feeds = {
global: {
@ -79,6 +80,8 @@ const Feeds = {
const feedOrder = (a, b) => b[0].createdAt - a[0].createdAt
const MAX_ITEMS_VISIBLE = 50
export default defineComponent({
name: 'Feed',
components: {
@ -110,14 +113,11 @@ export default defineComponent({
return this.feeds[this.selectedFeed]
},
feedItems() {
return this.activeFeed?.items
},
feedUnreads() {
return this.activeFeed?.unreads
return this.activeFeed?.visible
},
numUnreads() {
if (this.recentlyLoaded) return 0
return this.activeFeed?.unreads.length
return this.activeFeed?.newer.length
},
},
methods: {
@ -129,22 +129,27 @@ export default defineComponent({
filters,
{subId: `feed:${feedId}`}
)
stream.on('init', events => {
const items = events.map(event => [event]) // TODO Single element thread
stream.on('init', notes => {
const items = notes
.filter(this.filterNote.bind(this))
.map(note => [note]) // TODO Single element thread
items.sort(feedOrder)
this.feeds[feedId].items = items.slice(0, filters.limit)
this.feeds[feedId].visible = items.slice(0, filters.limit)
this.loading = false
// Wait a bit before showing the first unreads
setTimeout(() => this.recentlyLoaded = false, 5000)
})
stream.on('update', event => {
this.feeds[feedId].unreads.push([event]) // TODO Single element thread
stream.on('update', note => {
if (this.filterNote(note)) {
this.feeds[feedId].newer.push([note]) // TODO Single element thread
}
})
this.feeds[feedId] = {
items: [],
unreads: [],
visible: [],
newer: [],
older: [],
stream,
}
},
@ -152,12 +157,18 @@ export default defineComponent({
this.initFeed(feedId)
this.selectedFeed = feedId
},
loadUnreads() {
loadNewer() {
// TODO Deduplicate feed items
const items = this.feedUnreads.concat(this.feedItems)
this.activeFeed.newer.sort(feedOrder)
const items = this.activeFeed.newer.concat(this.feedItems)
if (items.length > MAX_ITEMS_VISIBLE) {
const older = items.splice(MAX_ITEMS_VISIBLE)
this.activeFeed.older = older.concat(this.activeFeed.older)
}
//items.sort(feedOrder)
this.activeFeed.items = items
this.activeFeed.unreads = []
this.activeFeed.visible = items
this.activeFeed.newer = []
// Wait a bit before showing unreads again
this.recentlyLoaded = true
@ -167,22 +178,35 @@ export default defineComponent({
},
async loadOlder() {
const until = this.feedItems[this.feedItems.length - 1]?.[0]?.createdAt || DateUtils.now()
console.log('until', new Date(until * 1000))
const filters = Object.assign({}, Feeds[this.selectedFeed].filters, {until})
if (this.activeFeed.older.length >= filters.limit) {
const chunk = this.activeFeed.older.splice(0, filters.limit)
this.activeFeed.visible = this.feedItems.concat(chunk)
return chunk
}
// Remove any residual older items
this.activeFeed.older = []
const older = await this.nostr.fetch(filters, {subId: `feed:${this.selectedFeed}-older`})
const items = older.map(event => [event]).sort(feedOrder)
const items = older
.filter(note => note.createdAt <= until)
.filter(this.filterNote.bind(this))
.map(note => [note]) // TODO Single element thread
.sort(feedOrder)
console.log('got items', older)
console.log('length before', this.activeFeed.items.length)
// TODO Deduplicate feed items
this.activeFeed.items = this.feedItems.concat(items)
console.log('length after', this.activeFeed.items.length)
this.activeFeed.visible = this.feedItems.concat(items)
return older
},
filterNote(note) {
if (note.isReaction()) return false
if (note.isRepostOrTag()) return false
if (note.relatedPubkeys().some(Bots.isBot)) return false
return true
}
},
mounted() {
this.initFeed(this.selectedFeed)

10
src/utils/bots.js Normal file
View File

@ -0,0 +1,10 @@
import {bech32ToHex} from 'src/utils/utils'
const Bots = new Set()
export default Bots
Bots.isBot = Bots.has.bind(Bots)
Bots.add = pubkey => Set.prototype.add.call(Bots, bech32ToHex(pubkey))
Bots.add('npub1tsgw6pncspg4d5u778hk63s3pls70evs4czfsmx0fzap9xwt203qtkhtk4') // gpt3
Bots.add('npub17stpezz4suqdywh33k9x8pht04l76a5sfrsjj7q3mnp5ap5937eqdt58d7') // bitcoin_bot
Bots.add('npub1xe59lfgsdvduqwh8h65zahkc2hv02mzpmdxghhhcpx0puret9taqheapxc') // moe_bot