Allow advanced filters to control entire feed

This commit is contained in:
Jonathan Staab 2023-06-13 15:14:31 -07:00
parent 8f96398419
commit 509e316ee6
3 changed files with 62 additions and 64 deletions

View File

@ -27,7 +27,7 @@
export let invertColors = false export let invertColors = false
export let onEvent = null export let onEvent = null
let sub, scroller, cursor, overrides let sub, scroller, cursor
let key = Math.random() let key = Math.random()
let search = "" let search = ""
let notes = [] let notes = []
@ -37,7 +37,6 @@
$: searchNotes = debounce(300, fuzzy(notes, {keys: ["content"]})) $: searchNotes = debounce(300, fuzzy(notes, {keys: ["content"]}))
$: filteredNotes = search ? searchNotes(search) : notes $: filteredNotes = search ? searchNotes(search) : notes
$: mergedFilter = mergeFilter(filter, overrides)
const since = now() const since = now()
const maxNotes = 100 const maxNotes = 100
@ -121,14 +120,14 @@
let p = Promise.resolve() let p = Promise.resolve()
// If we have a search term we need to use only relays that support search // If we have a search term we need to use only relays that support search
const getRelays = () => (overrides?.search ? [{url: "wss://relay.nostr.band"}] : relays) const getRelays = () => (filter.search ? [{url: "wss://relay.nostr.band"}] : relays)
const loadMore = async () => { const loadMore = async () => {
const _key = key const _key = key
// Wait for this page to load before trying again // Wait for this page to load before trying again
await cursor.loadPage({ await cursor.loadPage({
filter: mergedFilter, filter,
onChunk: chunk => { onChunk: chunk => {
// Stack promises to avoid too many concurrent subscriptions // Stack promises to avoid too many concurrent subscriptions
p = p.then(() => key === _key && onChunk(chunk)) p = p.then(() => key === _key && onChunk(chunk))
@ -145,19 +144,19 @@
key = Math.random() key = Math.random()
} }
const start = (_overrides = {}) => { const start = (newFilter = {}) => {
if (!equals(_overrides, overrides)) { if (!equals(newFilter, filter)) {
stop() stop()
const _key = key const _key = key
overrides = _overrides filter = {...filter, ...newFilter}
// No point in subscribing if we have an end date // No point in subscribing if we have an end date
if (!filter.until) { if (!filter.until) {
sub = network.listen({ sub = network.listen({
relays: getRelays(), relays: getRelays(),
filter: mergeFilter(mergedFilter, {since}), filter: mergeFilter(filter, {since}),
onChunk: chunk => { onChunk: chunk => {
p = p.then(() => _key === key && onChunk(chunk)) p = p.then(() => _key === key && onChunk(chunk))
}, },
@ -166,7 +165,7 @@
cursor = new network.Cursor({ cursor = new network.Cursor({
relays: getRelays(), relays: getRelays(),
until: overrides.until || now(), until: filter.until || now(),
delta, delta,
}) })
@ -193,8 +192,8 @@
{/if} {/if}
<div class="flex justify-between gap-4" in:fly={{y: 20}}> <div class="flex justify-between gap-4" in:fly={{y: 20}}>
<FilterSummary filter={mergedFilter} /> <FilterSummary {filter} />
<FeedAdvanced onChange={start} hide={Object.keys(filter)} /> <FeedAdvanced {filter} onChange={start} />
</div> </div>
<div class="flex flex-col gap-4"> <div class="flex flex-col gap-4">

View File

@ -1,6 +1,6 @@
<script lang="ts"> <script lang="ts">
import type {Filter} from "nostr-tools" import type {Filter} from "nostr-tools"
import {pluck} from "ramda" import {pluck, objOf} from "ramda"
import {debounce} from "throttle-debounce" import {debounce} from "throttle-debounce"
import {createLocalDate} from "src/util/misc" import {createLocalDate} from "src/util/misc"
import Input from "src/partials/Input.svelte" import Input from "src/partials/Input.svelte"
@ -11,32 +11,34 @@
import PersonBadge from "src/app/shared/PersonBadge.svelte" import PersonBadge from "src/app/shared/PersonBadge.svelte"
import {searchTopics, searchPeople, getPersonWithFallback} from "src/agent/db" import {searchTopics, searchPeople, getPersonWithFallback} from "src/agent/db"
export let hide = []
export let onChange export let onChange
export let filter = {} as Filter
let filter = { let _filter = {
since: null, since: filter.since,
until: null, until: filter.since,
authors: [], search: filter.search || "",
search: "", authors: (filter.authors || []).map(getPersonWithFallback),
"#t": [], "#t": (filter["#t"] || []).map(objOf("name")),
"#p": [], "#p": (filter["#p"] || []).map(getPersonWithFallback),
} }
let modal = null let modal = null
const applyFilter = debounce(300, () => { const applyFilter = debounce(300, () => {
if (modal !== "maxi") { if (modal !== "maxi") {
const _filter = {} as Filter const newFilter = {} as Filter
if (filter.since) _filter.since = createLocalDate(filter.since).setHours(23, 59, 59, 0) / 1000 if (_filter.since)
if (filter.until) _filter.until = createLocalDate(filter.until).setHours(23, 59, 59, 0) / 1000 newFilter.since = createLocalDate(_filter.since).setHours(23, 59, 59, 0) / 1000
if (filter.authors.length > 0) _filter.authors = pluck("pubkey", filter.authors) if (_filter.until)
if (filter.search) _filter.search = filter.search newFilter.until = createLocalDate(_filter.until).setHours(23, 59, 59, 0) / 1000
if (filter["#t"].length > 0) _filter["#t"] = pluck("name", filter["#t"]) if (_filter.authors.length > 0) newFilter.authors = pluck("pubkey", _filter.authors)
if (filter["#p"].length > 0) _filter["#p"] = pluck("pubkey", filter["#p"]) if (_filter.search) newFilter.search = _filter.search
if (_filter["#t"].length > 0) newFilter["#t"] = pluck("name", _filter["#t"])
if (_filter["#p"].length > 0) newFilter["#p"] = pluck("pubkey", _filter["#p"])
onChange(_filter) onChange(newFilter)
} }
}) })
@ -68,7 +70,7 @@
<Content size="lg"> <Content size="lg">
<div class="flex flex-col gap-1"> <div class="flex flex-col gap-1">
<strong>Search</strong> <strong>Search</strong>
<Input bind:value={filter.search} on:input={applyFilter}> <Input bind:value={_filter.search} on:input={applyFilter}>
<i slot="before" class="fa fa-search" /> <i slot="before" class="fa fa-search" />
</Input> </Input>
</div> </div>
@ -76,49 +78,43 @@
<div class="grid grid-cols-2 gap-2"> <div class="grid grid-cols-2 gap-2">
<div class="flex flex-col gap-1"> <div class="flex flex-col gap-1">
<strong>Since</strong> <strong>Since</strong>
<Input type="date" bind:value={filter.since} /> <Input type="date" bind:value={_filter.since} />
</div> </div>
<div class="flex flex-col gap-1"> <div class="flex flex-col gap-1">
<strong>Until</strong> <strong>Until</strong>
<Input type="date" bind:value={filter.until} /> <Input type="date" bind:value={_filter.until} />
</div> </div>
</div> </div>
{#if !hide.includes("authors")} <div class="flex flex-col gap-1">
<div class="flex flex-col gap-1"> <strong>Authors</strong>
<strong>Authors</strong> <MultiSelect search={$searchPeople} bind:value={_filter.authors}>
<MultiSelect search={$searchPeople} bind:value={filter.authors}> <div slot="item" let:item>
<div slot="item" let:item> <div class="-my-1">
<div class="-my-1"> <PersonBadge inert person={getPersonWithFallback(item.pubkey)} />
<PersonBadge inert person={getPersonWithFallback(item.pubkey)} />
</div>
</div> </div>
</MultiSelect> </div>
</div> </MultiSelect>
{/if} </div>
{#if !hide.includes("#t")} <div class="flex flex-col gap-1">
<div class="flex flex-col gap-1"> <strong>Topics</strong>
<strong>Topics</strong> <MultiSelect search={$searchTopics} bind:value={_filter["#t"]}>
<MultiSelect search={$searchTopics} bind:value={filter["#t"]}> <div slot="item" let:item>
<div slot="item" let:item> <div class="-my-1">
<div class="-my-1"> #{item.name}
#{item.name}
</div>
</div> </div>
</MultiSelect> </div>
</div> </MultiSelect>
{/if} </div>
{#if !hide.includes("#p")} <div class="flex flex-col gap-1">
<div class="flex flex-col gap-1"> <strong>Mentions</strong>
<strong>Mentions</strong> <MultiSelect search={$searchPeople} bind:value={_filter["#p"]}>
<MultiSelect search={$searchPeople} bind:value={filter["#p"]}> <div slot="item" let:item>
<div slot="item" let:item> <div class="-my-1">
<div class="-my-1"> <PersonBadge inert person={getPersonWithFallback(item.pubkey)} />
<PersonBadge inert person={getPersonWithFallback(item.pubkey)} />
</div>
</div> </div>
</MultiSelect> </div>
</div> </MultiSelect>
{/if} </div>
<div class="flex justify-end"> <div class="flex justify-end">
<Anchor type="button-accent" on:click={submit}>Apply Filters</Anchor> <Anchor type="button-accent" on:click={submit}>Apply Filters</Anchor>
</div> </div>

View File

@ -50,6 +50,9 @@
</script> </script>
<div> <div>
{#if parts.length > 0}
<span class="mr-2 mb-2"> Showing notes: </span>
{/if}
{#each parts as part} {#each parts as part}
<Chip class="mr-2 mb-2">{part}</Chip> <Chip class="mr-2 mb-2">{part}</Chip>
{/each} {/each}