mirror of
https://github.com/styppo/hamstr.git
synced 2024-10-18 05:23:28 +00:00
* Hide reactions, reposts, bots from feed
* Improve feed performance
This commit is contained in:
parent
ecbb2f98e3
commit
2e78bdd51e
@ -44,7 +44,6 @@ export default defineComponent({
|
|||||||
methods: {
|
methods: {
|
||||||
async load() {
|
async load() {
|
||||||
if (this.loading) return
|
if (this.loading) return
|
||||||
console.log('loading')
|
|
||||||
|
|
||||||
this.loading = true
|
this.loading = true
|
||||||
this.$emit('loading')
|
this.$emit('loading')
|
||||||
|
@ -41,21 +41,28 @@ export default class Note {
|
|||||||
return this.eventRefs().ancestor()
|
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() {
|
pubkeyRefs() {
|
||||||
return this.tags
|
return this.pubkeyTags().map(tag => tag.ref)
|
||||||
.filter(tag => tag.type === TagType.PUBKEY)
|
|
||||||
.map(tag => tag.ref)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
eventRefs() {
|
eventRefs() {
|
||||||
const refs = this.tags
|
return new EventRefs(this.eventTags().map(tag => tag.ref))
|
||||||
.filter(tag => tag.type === TagType.EVENT)
|
}
|
||||||
.map(tag => tag.ref)
|
|
||||||
return new EventRefs(refs)
|
relatedPubkeys() {
|
||||||
|
return [this.author].concat(this.pubkeyRefs())
|
||||||
}
|
}
|
||||||
|
|
||||||
contentTagRefs() {
|
contentTagRefs() {
|
||||||
const regex = /#\[([0-9]+)]/ig
|
const regex = /#\[([0-9]+)]/g
|
||||||
let refs = []
|
let refs = []
|
||||||
let match
|
let match
|
||||||
while ((match = regex.exec(this.content))) {
|
while ((match = regex.exec(this.content))) {
|
||||||
@ -64,9 +71,16 @@ export default class Note {
|
|||||||
return refs
|
return refs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isRepostOrTag() {
|
||||||
|
return Note.isRepostOrTag(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
static isRepostOrTag(event) {
|
||||||
|
return /#\[([0-9]+)]/.test(event.content)
|
||||||
|
}
|
||||||
|
|
||||||
isReaction() {
|
isReaction() {
|
||||||
return this.kind === EventKind.REACTION
|
return Note.isReaction(this)
|
||||||
|| (this.hasAncestor() && Note.isReactionContent(this.content))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static isReaction(event) {
|
static isReaction(event) {
|
||||||
|
@ -22,6 +22,9 @@ export const useStatStore = defineStore('stat', {
|
|||||||
if (Note.isReaction(event)) {
|
if (Note.isReaction(event)) {
|
||||||
const stats = this.getOrInit(event.eventRefs().ancestor())
|
const stats = this.getOrInit(event.eventRefs().ancestor())
|
||||||
stats.reactions++
|
stats.reactions++
|
||||||
|
} else if (Note.isRepostOrTag(event)) {
|
||||||
|
const stats = this.getOrInit(event.eventRefs().ancestor())
|
||||||
|
stats.shares++
|
||||||
} else {
|
} else {
|
||||||
for (const eventId of event.eventRefs()) {
|
for (const eventId of event.eventRefs()) {
|
||||||
const stats = this.getOrInit(eventId)
|
const stats = this.getOrInit(eventId)
|
||||||
|
@ -33,13 +33,13 @@
|
|||||||
<div class="load-more-container" :class="{'more-available': numUnreads}">
|
<div class="load-more-container" :class="{'more-available': numUnreads}">
|
||||||
<AsyncLoadButton
|
<AsyncLoadButton
|
||||||
v-if="numUnreads"
|
v-if="numUnreads"
|
||||||
:load-fn="loadUnreads"
|
:load-fn="loadNewer"
|
||||||
:label="`Load ${numUnreads} unreads`"
|
:label="`Load ${numUnreads} unreads`"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<template v-for="(thread, i) in feedItems">
|
<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>
|
</template>
|
||||||
|
|
||||||
<ListPlaceholder :count="feedItems?.length" :loading="loading" />
|
<ListPlaceholder :count="feedItems?.length" :loading="loading" />
|
||||||
@ -66,6 +66,7 @@ import {useNostrStore} from 'src/nostr/NostrStore'
|
|||||||
import Defer from 'src/utils/Defer'
|
import Defer from 'src/utils/Defer'
|
||||||
import {EventKind} from 'src/nostr/model/Event'
|
import {EventKind} from 'src/nostr/model/Event'
|
||||||
import DateUtils from 'src/utils/DateUtils'
|
import DateUtils from 'src/utils/DateUtils'
|
||||||
|
import Bots from 'src/utils/bots'
|
||||||
|
|
||||||
const Feeds = {
|
const Feeds = {
|
||||||
global: {
|
global: {
|
||||||
@ -79,6 +80,8 @@ const Feeds = {
|
|||||||
|
|
||||||
const feedOrder = (a, b) => b[0].createdAt - a[0].createdAt
|
const feedOrder = (a, b) => b[0].createdAt - a[0].createdAt
|
||||||
|
|
||||||
|
const MAX_ITEMS_VISIBLE = 50
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'Feed',
|
name: 'Feed',
|
||||||
components: {
|
components: {
|
||||||
@ -110,14 +113,11 @@ export default defineComponent({
|
|||||||
return this.feeds[this.selectedFeed]
|
return this.feeds[this.selectedFeed]
|
||||||
},
|
},
|
||||||
feedItems() {
|
feedItems() {
|
||||||
return this.activeFeed?.items
|
return this.activeFeed?.visible
|
||||||
},
|
|
||||||
feedUnreads() {
|
|
||||||
return this.activeFeed?.unreads
|
|
||||||
},
|
},
|
||||||
numUnreads() {
|
numUnreads() {
|
||||||
if (this.recentlyLoaded) return 0
|
if (this.recentlyLoaded) return 0
|
||||||
return this.activeFeed?.unreads.length
|
return this.activeFeed?.newer.length
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
@ -129,22 +129,27 @@ export default defineComponent({
|
|||||||
filters,
|
filters,
|
||||||
{subId: `feed:${feedId}`}
|
{subId: `feed:${feedId}`}
|
||||||
)
|
)
|
||||||
stream.on('init', events => {
|
stream.on('init', notes => {
|
||||||
const items = events.map(event => [event]) // TODO Single element thread
|
const items = notes
|
||||||
|
.filter(this.filterNote.bind(this))
|
||||||
|
.map(note => [note]) // TODO Single element thread
|
||||||
items.sort(feedOrder)
|
items.sort(feedOrder)
|
||||||
this.feeds[feedId].items = items.slice(0, filters.limit)
|
this.feeds[feedId].visible = items.slice(0, filters.limit)
|
||||||
this.loading = false
|
this.loading = false
|
||||||
|
|
||||||
// Wait a bit before showing the first unreads
|
// Wait a bit before showing the first unreads
|
||||||
setTimeout(() => this.recentlyLoaded = false, 5000)
|
setTimeout(() => this.recentlyLoaded = false, 5000)
|
||||||
})
|
})
|
||||||
stream.on('update', event => {
|
stream.on('update', note => {
|
||||||
this.feeds[feedId].unreads.push([event]) // TODO Single element thread
|
if (this.filterNote(note)) {
|
||||||
|
this.feeds[feedId].newer.push([note]) // TODO Single element thread
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
this.feeds[feedId] = {
|
this.feeds[feedId] = {
|
||||||
items: [],
|
visible: [],
|
||||||
unreads: [],
|
newer: [],
|
||||||
|
older: [],
|
||||||
stream,
|
stream,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -152,12 +157,18 @@ export default defineComponent({
|
|||||||
this.initFeed(feedId)
|
this.initFeed(feedId)
|
||||||
this.selectedFeed = feedId
|
this.selectedFeed = feedId
|
||||||
},
|
},
|
||||||
loadUnreads() {
|
loadNewer() {
|
||||||
// TODO Deduplicate feed items
|
// 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)
|
//items.sort(feedOrder)
|
||||||
this.activeFeed.items = items
|
|
||||||
this.activeFeed.unreads = []
|
this.activeFeed.visible = items
|
||||||
|
this.activeFeed.newer = []
|
||||||
|
|
||||||
// Wait a bit before showing unreads again
|
// Wait a bit before showing unreads again
|
||||||
this.recentlyLoaded = true
|
this.recentlyLoaded = true
|
||||||
@ -167,22 +178,35 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
async loadOlder() {
|
async loadOlder() {
|
||||||
const until = this.feedItems[this.feedItems.length - 1]?.[0]?.createdAt || DateUtils.now()
|
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})
|
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 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
|
// TODO Deduplicate feed items
|
||||||
this.activeFeed.items = this.feedItems.concat(items)
|
this.activeFeed.visible = this.feedItems.concat(items)
|
||||||
|
|
||||||
console.log('length after', this.activeFeed.items.length)
|
|
||||||
|
|
||||||
return older
|
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() {
|
mounted() {
|
||||||
this.initFeed(this.selectedFeed)
|
this.initFeed(this.selectedFeed)
|
||||||
|
10
src/utils/bots.js
Normal file
10
src/utils/bots.js
Normal 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
|
Loading…
Reference in New Issue
Block a user