+
Skip to content

Fix feeds #465

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Nov 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .env

Large diffs are not rendered by default.

42 changes: 21 additions & 21 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,15 +40,15 @@
"@types/ramda": "^0.30.2",
"@types/throttle-debounce": "^5.0.2",
"@vite-pwa/assets-generator": "^0.2.6",
"@welshman/app": "~0.0.21",
"@welshman/app": "~0.0.23",
"@welshman/content": "~0.0.12",
"@welshman/dvm": "~0.0.10",
"@welshman/feeds": "~0.0.21",
"@welshman/feeds": "~0.0.23",
"@welshman/lib": "~0.0.24",
"@welshman/net": "~0.0.33",
"@welshman/signer": "~0.0.11",
"@welshman/store": "~0.0.12",
"@welshman/util": "~0.0.44",
"@welshman/util": "~0.0.45",
"autoprefixer": "^10.4.20",
"bowser": "^2.11.0",
"classnames": "^2.5.1",
Expand Down
6 changes: 1 addition & 5 deletions src/app/MenuMobile.svelte
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
<script lang="ts">
import {makeScopeFeed, Scope} from "@welshman/feeds"
import {signer, pubkey, sessions, displayProfileByPubkey} from "@welshman/app"
import {toggleTheme, installPrompt, installAsPWA} from "src/partials/state"
import Anchor from "src/partials/Anchor.svelte"
Expand All @@ -10,7 +9,6 @@
import MenuMobileItem from "src/app/MenuMobileItem.svelte"
import {slowConnections, menuIsOpen} from "src/app/state"
import {router} from "src/app/util/router"
import {makeFeed, normalizeFeedDefinition} from "src/domain"
import {env, hasNewMessages, hasNewNotifications} from "src/engine"

const closeSubMenu = () => {
Expand All @@ -35,9 +33,7 @@
}

const openFeeds = () => {
const feed = makeFeed({definition: normalizeFeedDefinition(makeScopeFeed(Scope.Follows))})

router.at("notes").cx({feed}).push()
router.at("notes").push()
closeMenu()
}

Expand Down
193 changes: 159 additions & 34 deletions src/app/shared/Feed.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -2,49 +2,114 @@
import {onMount} from "svelte"
import {seconds} from "hurdak"
import {writable} from "svelte/store"
import {now, hash} from "@welshman/lib"
import {ctx, now, identity, uniqBy, range, hash, pushToMapKey} from "@welshman/lib"
import {
neverFilter,
getIdFilters,
REACTION,
getIdOrAddress,
getIdAndAddress,
getAncestorTagValues,
unionFilters,
} from "@welshman/util"
import type {Filter, TrustedEvent} from "@welshman/util"
import type {FeedController, Feed as FeedDefinition} from "@welshman/feeds"
import {
isRelayFeed,
makeKindFeed,
makeIntersectionFeed,
isKindFeed,
walkFeed,
} from "@welshman/feeds"
import {repository} from "@welshman/app"
import {createScroller, synced} from "src/util/misc"
import {noteKinds, repostKinds, reactionKinds, isLike} from "src/util/nostr"
import {fly, fade} from "src/util/transition"
import Anchor from "src/partials/Anchor.svelte"
import Card from "src/partials/Card.svelte"
import Spinner from "src/partials/Spinner.svelte"
import FlexColumn from "src/partials/FlexColumn.svelte"
import Note from "src/app/shared/Note.svelte"
import FeedControls from "src/app/shared/FeedControls.svelte"
import {createFeed, router} from "src/app/util"
import {router} from "src/app/util"
import type {Feed} from "src/domain"
import {env} from "src/engine"
import {
env,
load,
createFeedController,
sortEventsDesc,
isEventMuted,
unwrapRepost,
} from "src/engine"

export let feed: Feed
export let anchor = null
export let eager = false
export let skipNetwork = false
export let forcePlatform = true
export let showControls = false
export let forcePlatform = true
export let hideSpinner = false
export let includeReposts = false
export let onEvent = null

const reposts = new Map<string, TrustedEvent[]>()

const splits = [["zap", env.PLATFORM_PUBKEY, "", "1"]]

const promptDismissed = synced("feed/promptDismissed", 0)

const shouldHideReplies = showControls ? synced("Feed.shouldHideReplies", false) : writable(false)

const reload = async () => {
limit = 0
loader?.stop()
loader = createFeed({
anchor,
onEvent,
skipNetwork,
const reload = () => {
exhausted = false
useWindowing = true
events = []
buffer = []
filters = [neverFilter]

let hasKinds = false

walkFeed(feed.definition, (subFeed: FeedDefinition) => {
hasKinds = hasKinds || isKindFeed(subFeed)
useWindowing = useWindowing && !isRelayFeed(subFeed)
})

const definition = hasKinds
? feed.definition
: makeIntersectionFeed(makeKindFeed(...noteKinds), feed.definition)

ctrl = createFeedController({
feed: definition,
forcePlatform,
includeReposts,
shouldDefer: !eager,
shouldLoadParents: true,
shouldHideReplies: $shouldHideReplies,
feed: feed.definition,
useWindowing,
onEvent: async e => {
if (e.kind === REACTION && !isLike(e)) return false
if (repository.isDeleted(e)) return false
if ($isEventMuted(e, true)) return false

const {replies} = getAncestorTagValues(e.tags)

if ($shouldHideReplies && replies.length > 0) return false

if (replies.length > 0 && !replies.find(id => repository.getEvent(id))) {
await load({
filters: getIdFilters(replies),
relays: ctx.app.router.EventParents(e).getUrls(),
})
}

buffer.push(e)
},
onExhausted: () => {
exhausted = true
},
})

ctrl.getRequestItems().then($requestItems => {
if ($requestItems) {
filters = unionFilters($requestItems.flatMap(item => item.filters))
}
})

if (!useWindowing) {
ctrl.load(1000)
}
}

const toggleReplies = () => {
Expand All @@ -57,24 +122,88 @@
reload()
}

const getNewEvents = () => {
const seen = new Set(events.map(getIdOrAddress))

const isSeen = (e: TrustedEvent) => {
if (getIdAndAddress(e).some(v => seen.has(v))) return true
if (getAncestorTagValues(e.tags).replies.some(v => seen.has(v))) return true

return false
}

const unwrap = (e: TrustedEvent) => {
if (repostKinds.includes(e.kind)) {
const wrappedEvent = unwrapRepost(e)

if (wrappedEvent) {
pushToMapKey(reposts, wrappedEvent.id, e)
e = wrappedEvent
}
}

return e
}

return buffer
.splice(0, 10)
.map((e: TrustedEvent) => {
// If we have a repost, use its contents instead
e = unwrap(e)

if (isSeen(e)) return undefined

Array.from(range(0, 2)).forEach(() => {
const parent = getAncestorTagValues(e.tags)
.replies.map(v => repository.getEvent(v))
.find(identity)

// If we have a parent, show that instead, with replies grouped underneath
if (parent) {
e = unwrap(parent)
}
})

if (isSeen(e)) return undefined
if (repostKinds.includes(e.kind)) return undefined
if (reactionKinds.includes(e.kind)) return undefined

for (const v of getIdAndAddress(e)) {
seen.add(v)
}

return e
})
.filter(identity)
}

const loadMore = async () => {
limit += 10
buffer = uniqBy(e => e.id, sortEventsDesc(buffer))
events = [...events, ...getNewEvents()]

if ($loader.notes.length < limit) {
await loader.loadMore(20)
if (useWindowing && buffer.length < 50) {
ctrl.load(50)
}
}

let element, loader
let limit = 0
let element
let exhausted = false
let useWindowing = true
let ctrl: FeedController
let events: TrustedEvent[] = []
let buffer: TrustedEvent[] = []
let filters: Filter[] = [neverFilter]

reload()

onMount(() => {
const scroller = createScroller(loadMore, {element})
const scroller = createScroller(loadMore, {
element,
delay: 300,
threshold: 3000,
})

return () => {
loader.stop()
scroller.stop()
}
})
Expand All @@ -93,13 +222,9 @@
{/if}

<FlexColumn xl bind:element>
{#each $loader.notes.slice(0, limit) as note, i (note.id)}
{#each events as note, i (note.id)}
<div in:fly={{y: 20}}>
<Note
filters={loader.getFilters() || [{ids: []}]}
depth={$shouldHideReplies ? 0 : 2}
{anchor}
{note} />
<Note {filters} {reposts} depth={$shouldHideReplies ? 0 : 2} {anchor} {note} />
</div>
{#if i > 20 && parseInt(hash(note.id)) % 100 === 0 && $promptDismissed < now() - seconds(7, "day")}
<Card class="group flex items-center justify-between">
Expand All @@ -120,7 +245,7 @@
</FlexColumn>

{#if !hideSpinner}
{#if $loader.done}
{#if exhausted}
<div transition:fly|local={{y: 20, delay: 500}} class="flex flex-col items-center py-24">
<img alt="" class="h-20 w-20" src="/images/pumpkin.png" />
That's all!
Expand Down
3 changes: 2 additions & 1 deletion src/app/shared/Note.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
export let note
export let relays = []
export let filters = null
export let reposts = new Map()
export let depth = 0
export let anchor = null
export let topLevel = false
Expand Down Expand Up @@ -211,7 +212,7 @@
{@const showReply = reply && !ancestors.replies.includes(anchor) && showParent}
{@const showRoot = root && !ancestors.roots.includes(anchor) && root !== reply && showParent}
<div>
<NoteMeta note={event} />
<NoteMeta {reposts} note={event} />
<div class="note relative">
{#if !showParent && !topLevel}
<AltColor let:isAlt>
Expand Down
Loading
点击 这是indexloc提供的php浏览器服务,不要输入任何密码和下载