Work on recursive feed editor

This commit is contained in:
Jon Staab 2024-04-17 08:49:51 -07:00
parent 036677cc2f
commit 051b406f2b
20 changed files with 440 additions and 54 deletions

3
.editorconfig Normal file
View File

@ -0,0 +1,3 @@
[*]
indent_style = space
indent_size = 2

View File

@ -18,7 +18,13 @@
export let value
const isOpen = writable(false)
const openModal = () => {
isOpen = true
}
const closeModal = () => {
isOpen = false
}
const toggleReplies = () => {
value = {...value, shouldHideReplies: !value.shouldHideReplies}
@ -32,15 +38,27 @@
feed = [feed[0], omit(keys, feed[1])] as Feed
}
const setFeed = f => {
feed = f
}
const saveFeed = () => {
value = {...value, feed}
}
const setAndSaveFeed = f => {
setFeed(f)
saveFeed()
closeModal()
}
const displayPeople = pubkeys =>
pubkeys.length === 1 ? displayPubkey(pubkeys[0]) : `${pubkeys.length} people`
const displayTopics = topics => (topics.length === 1 ? topics[0] : `${topics.length} topics`)
let isOpen = false
$: feed = value.feed
$: feedType = feed[0]
$: subFeeds = getSubFeeds(feed)
@ -54,7 +72,7 @@
<Toggle scale={0.6} value={!value.shouldHideReplies} on:change={toggleReplies} />
<small class="text-neutral-200">Show replies</small>
</div>
<i class="fa fa-search cursor-pointer p-2" on:click={() => isOpen.set(true)} />
<i class="fa fa-search cursor-pointer p-2" on:click={openModal} />
<slot name="controls" />
</div>
<div class="mb-2 mr-2 inline-block py-1">Showing notes:</div>
@ -116,7 +134,7 @@
<MenuItem on:click={() => setPart({scopes: [Scope.Global]})}>
<i class="fa fa-earth-americas mr-2" /> Global
</MenuItem>
<MenuItem on:click={() => isOpen.set(true)}>
<MenuItem on:click={openModal}>
<i class="fa fa-cog mr-2" /> Custom
</MenuItem>
</Menu>
@ -157,16 +175,16 @@
{/if}
<div
class="inline-block rounded-full border border-neutral-100"
on:click={() => isOpen.set(true)}>
on:click={openModal}>
<div class="flex h-7 w-7 items-center justify-center">
<i class="fa fa-plus cursor-pointer" />
</div>
</div>
</div>
{#if $isOpen}
<Modal onEscape={() => isOpen.set(false)}>
{#if isOpen}
<Modal onEscape={closeModal}>
<Subheading>Customize Feed</Subheading>
<FeedForm {feed} />
<FeedForm {feed} onCancel={closeModal} onChange={setAndSaveFeed} />
</Modal>
{/if}

View File

@ -1,34 +1,63 @@
<script lang="ts">
import {FeedType, getSubFeeds} from '@coracle.social/feeds'
import {quantify, switcherFn} from 'hurdak'
import {inc} from "@coracle.social/lib"
import {FeedType, hasSubFeeds, getSubFeeds} from "@coracle.social/feeds"
import Card from "src/partials/Card.svelte"
import Popover from "src/partials/Popover.svelte"
import Menu from "src/partials/Menu.svelte"
import MenuItem from "src/partials/MenuItem.svelte"
import Anchor from "src/partials/Anchor.svelte"
import FlexColumn from "src/partials/FlexColumn.svelte"
import Field from "src/partials/Field.svelte"
import FieldInline from "src/partials/FieldInline.svelte"
import Select from "src/partials/Select.svelte"
import SearchSelect from "src/partials/SearchSelect.svelte"
import {searchRelayUrls, displayRelayUrl} from 'src/engine'
import FilterField from "src/app/shared/FilterField.svelte"
import {searchRelayUrls, displayRelayUrl} from "src/engine"
export let feed
export let onChange
export let onCancel
const onTypeChange = e => {
feed = [e.detail]
const addFeed = feedType => {
feed = [...feed, [feedType]]
}
const onRelayChange = e => {
feed[1] = e.detail
const onTypeChange = type => {
feed = [type]
}
const onRelayChange = urls => {
feed[1] = urls
}
const onFilterChange = (filter, i) => {
feed[i] = filter
}
const displayFeed = ([type, ...feed]) =>
switcherFn(type, {
[FeedType.Filter]: () => quantify(feed.length, 'filter'),
[FeedType.List]: () => quantify(feed.length, 'list'),
[FeedType.LOL]: () => quantify(feed.length, 'list') + ' of lists',
[FeedType.DVM]: () => quantify(feed.length, 'DVM'),
[FeedType.Relay]: () => quantify(feed.slice(1).length, 'feed') + ' on ' + quantify(feed[0].length, 'relays'),
[FeedType.Union]: () => 'union of ' + quantify(feed.length, 'feed'),
[FeedType.Intersection]: () => 'union of ' + quantify(feed.length, 'feed'),
[FeedType.Difference]: () => 'union of ' + quantify(feed.length, 'feed'),
[FeedType.SymmetricDifference]: () => 'union of ' + quantify(feed.length, 'feed'),
})
$: feedType = feed[0]
$: subFeeds = getSubFeeds(feed)
$: console.log(feedType, feed)
</script>
<FlexColumn>
<FlexColumn class="pb-32">
<Field label="Feed Type">
<Select value={feedType} on:change={onTypeChange}>
<option value={FeedType.Filter}>Filter</option>
<Select value={feedType} onChange={onTypeChange}>
<option value={FeedType.Filter}>Standard</option>
<option value={FeedType.List}>List</option>
<option value={FeedType.LOL}>List of lists</option>
<option value={FeedType.DVM}>Data vending machine</option>
@ -39,37 +68,59 @@
<option value={FeedType.SymmetricDifference}>Symmetric Difference</option>
</Select>
<p slot="info">
Select which feed type you'd like to use. In addition to some basic feed types,
nostr also supports combinations of sub-feeds using set operators for advanced
use cases.
<p>
</Field>
Select which feed type you'd like to use. In addition to some basic feed types, nostr also
supports combinations of sub-feeds using set operators for advanced use cases.
</p>
<p></p></Field>
{#if feedType === FeedType.Relay}
<Field label="Relay Selections">
<SearchSelect
multiple
value={feed[1] || []}
search={$searchRelayUrls}
on:change={onRelayChange}>
onChange={onRelayChange}>
<span slot="item" let:item>{displayRelayUrl(item)}</span>
</SearchSelect>
<p slot="info">
Select which relays you'd like to limit loading feeds from.
<p>
</Field>
<p slot="info">Select which relays you'd like to limit loading feeds from.</p>
<p></p></Field>
{:else if feedType === FeedType.Filter}
{#each feed.slice(1) as filter}
<FilterForm {feed} />
{#each feed.slice(1) as filter, i}
<Card>
<FilterField {filter} onChange={filter => onFilterChange(filter, inc(i))} />
</Card>
{/each}
{:else if feedType === FeedType.List}
{:else if feedType === FeedType.LOL}
{:else if feedType === FeedType.DVM}
{/if}
{#each subFeeds as subFeed, i}
<Card>
<Card class="flex justify-between items-center">
<span class="text-lg">{displayFeed(subFeed)}</span>
<Anchor class="flex gap-2 items-center" on:click={() => setCursor(subFeed)}>
<i class="fa fa-edit" /> Edit
</Anchor>
</Card>
{#if i < subFeeds.length - 1}
<p class="staatliches">OR</p>
<p class="staatliches text-center">OR</p>
{/if}
{/each}
<div class="flex justify-end items-center gap-3">
{#if hasSubFeeds(feed)}
<Popover theme="transparent" opts={{hideOnClick: true}}>
<span slot="trigger" class="cursor-pointer">
<i class="fa fa-plus" /> Add feed
</span>
<div slot="tooltip">
<Menu>
<MenuItem on:click={() => addFeed(FeedType.Filter)}>Standard Feed</MenuItem>
<MenuItem on:click={() => addFeed(FeedType.List)}>List Feed</MenuItem>
<MenuItem on:click={() => addFeed(FeedType.LOL)}>Lists of Lists</MenuItem>
<MenuItem on:click={() => addFeed(FeedType.DVM)}>DVM Feed</MenuItem>
</Menu>
</div>
</Popover>
{/if}
<Anchor button on:click={() => onCancel()}>Cancel</Anchor>
<Anchor button accent on:click={() => onChange(feed)}>Save</Anchor>
</div>
</FlexColumn>

View File

@ -0,0 +1,44 @@
<script lang="ts">
import {ucFirst} from 'hurdak'
import {omit, without, prop, last} from "ramda"
import {Scope} from '@coracle.social/feeds'
import {fuzzy} from 'src/util/misc'
import Field from "src/partials/Field.svelte"
import SearchSelect from "src/partials/SearchSelect.svelte"
import SelectButton from "src/partials/SelectButton.svelte"
import PersonBadge from "src/app/shared/PersonBadge.svelte"
import {searchPubkeys, displayPubkey} from 'src/engine'
export let filter
export let onChange
export let onRemove
const scopeOptions = (Object.values(Scope) as string[]).concat(['custom'])
const changeAuthors = authors => onChange(omit(['scopes'], {...filter, authors}))
const changeScopes = scopes =>
last(scopes) === 'custom'
? changeAuthors([])
: onChange(omit(['authors'], {...filter, scopes: without(['custom'], scopes)}))
$: scopes = filter.scopes || ['custom']
</script>
<Field>
<span slot="label" class="flex gap-2 items-center cursor-pointer" on:click={onRemove}>
<i class="fa fa-trash fa-sm" /> Authors
</span>
<SelectButton multiple onChange={changeScopes} value={scopes} options={scopeOptions} displayOption={ucFirst} />
{#if !filter.scopes}
<SearchSelect multiple search={$searchPubkeys} value={filter.authors || []} onChange={changeAuthors}>
<div slot="item" let:item let:context>
{#if context === "value"}
{displayPubkey(item)}
{:else}
<PersonBadge inert pubkey={item} />
{/if}
</div>
</SearchSelect>
{/if}
</Field>

View File

@ -0,0 +1,114 @@
<script lang="ts">
import {omit, without} from "ramda"
import Field from "src/partials/Field.svelte"
import Input from "src/partials/Input.svelte"
import Popover from "src/partials/Popover.svelte"
import Menu from "src/partials/Menu.svelte"
import MenuItem from "src/partials/MenuItem.svelte"
import FlexColumn from "src/partials/FlexColumn.svelte"
import FilterKindsField from "src/app/shared/FilterKindsField.svelte"
import FilterSearchField from "src/app/shared/FilterSearchField.svelte"
import FilterTopicsField from "src/app/shared/FilterTopicsField.svelte"
import FilterAuthorsField from "src/app/shared/FilterAuthorsField.svelte"
import FilterMentionsField from "src/app/shared/FilterMentionsField.svelte"
import FilterTimeframeField from "src/app/shared/FilterTimeframeField.svelte"
export let filter
export let onChange
const removeSection = section => {
sections = without([section], sections)
if (section === "kinds") {
filter = omit(['kinds'], filter)
}
if (section === "search") {
filter = omit(['search'], filter)
}
if (section === "topics") {
filter = omit(['#t'], filter)
}
if (section === "authors") {
filter = omit(['authors', 'scopes'], filter)
}
if (section === "mentions") {
filter = omit(['#p'], filter)
}
if (section === "timeframe") {
filter = omit(['since', 'until', 'since_ago', 'until_ago'], filter)
}
onChange(filter)
}
const addSection = section => {
sections = [...sections, section]
}
let sections = []
if (filter.kinds) sections.push("kinds")
if (filter.search) sections.push("search")
if (filter["#t"]) sections.push("topics")
if (filter.authors || filter.scopes) sections.push("authors")
if (filter["#p"]) sections.push("mentions")
if (filter.since || filter.until || filter.since_ago || filter.until_ago) {
sections.push("timeframe")
}
</script>
<FlexColumn>
{#each sections as section}
<div class="relative">
{#if section === "kinds"}
<FilterKindsField {filter} {onChange} onRemove={() => removeSection("kinds")} />
{:else if section === "search"}
<FilterSearchField {filter} {onChange} onRemove={() => removeSection("search")} />
{:else if section === "topics"}
<FilterTopicsField {filter} {onChange} onRemove={() => removeSection("topics")} />
{:else if section === "authors"}
<FilterAuthorsField {filter} {onChange} onRemove={() => removeSection("authors")} />
{:else if section === "mentions"}
<FilterMentionsField {filter} {onChange} onRemove={() => removeSection("mentions")} />
{:else if section === "topics"}
<FilterTopicsField {filter} {onChange} onRemove={() => removeSection("topics")} />
{:else if section === "timeframe"}
<FilterTimeframeField {filter} {onChange} onRemove={() => removeSection("timeframe")} />
{/if}
</div>
{/each}
<div>
<Popover theme="transparent" opts={{hideOnClick: true}} class="inline">
<span slot="trigger" class="cursor-pointer">
<i class="fa fa-plus" /> Add filter
</span>
<div slot="tooltip">
<Menu>
{#if !sections.includes("topics")}
<MenuItem on:click={() => addSection("topics")}>Topics</MenuItem>
{/if}
{#if !sections.includes("authors")}
<MenuItem on:click={() => addSection("authors")}>Authors</MenuItem>
{/if}
{#if !sections.includes("mentions")}
<MenuItem on:click={() => addSection("mentions")}>Mentions</MenuItem>
{/if}
{#if !sections.includes("timeframe")}
<MenuItem on:click={() => addSection("timeframe")}>Timeframe</MenuItem>
{/if}
{#if !sections.includes("search")}
<MenuItem on:click={() => addSection("search")}>Search</MenuItem>
{/if}
{#if !sections.includes("kinds")}
<MenuItem on:click={() => addSection("kinds")}>Kinds</MenuItem>
{/if}
</Menu>
</div>
</Popover>
</div>
</FlexColumn>

View File

@ -0,0 +1,51 @@
<script lang="ts">
import {omit, prop} from "ramda"
import {fuzzy} from 'src/util/misc'
import Field from "src/partials/Field.svelte"
import SearchSelect from "src/partials/SearchSelect.svelte"
export let filter
export let onChange
export let onRemove
const change = kinds => onChange({...filter, kinds})
const kinds = [
{label: "Note", kind: 1},
{label: "Profile", kind: 0},
{label: "Reaction", kind: 7},
{label: "Live chat", kind: 1311},
{label: "Remix", kind: 1808},
{label: "Audio", kind: 31337},
{label: "Report", kind: 1984},
{label: "Label", kind: 1985},
{label: "Review", kind: 1986},
{label: "Highlight", kind: 9802},
{label: "Article", kind: 30023},
{label: "Live event", kind: 30311},
{label: "Status", kind: 30315},
{label: "Listing", kind: 30402},
{label: "Calendar event (date based)", kind: 31922},
{label: "Calendar event (time based)", kind: 31923},
{label: "Calendar event RSVP", kind: 31925},
{label: "Handler recommendation", kind: 31989},
{label: "Handler information", kind: 31990},
{label: "Community definition", kind: 34550},
{label: "Group definition", kind: 35834},
{label: "Image", kind: 1063},
{label: "Relay selections", kind: 10002},
]
const searchKindItems = fuzzy(kinds, {keys: ["kind", "label"]})
const searchKinds = term => pluck('kind', searchKinds(term))
</script>
<Field>
<span slot="label" class="flex gap-2 items-center cursor-pointer" on:click={onRemove}>
<i class="fa fa-trash fa-sm" /> Kinds
</span>
<SearchSelect multiple search={searchKinds} value={filter.kinds || []} onChange={change}>
<div slot="item" let:item>{kinds.find(k => k.kind === item)?.label} (kind {item})</div>
</SearchSelect>
</Field>

View File

@ -0,0 +1,30 @@
<script lang="ts">
import {prop} from "ramda"
import {Scope} from '@coracle.social/feeds'
import {fuzzy} from 'src/util/misc'
import Field from "src/partials/Field.svelte"
import SearchSelect from "src/partials/SearchSelect.svelte"
import PersonBadge from "src/app/shared/PersonBadge.svelte"
import {searchPubkeys, displayPubkey} from 'src/engine'
export let filter
export let onChange
export let onRemove
const change = pubkeys => onChange({...filter, '#p': pubkeys})
</script>
<Field>
<span slot="label" class="flex gap-2 items-center cursor-pointer" on:click={onRemove}>
<i class="fa fa-trash fa-sm" /> Mentions
</span>
<SearchSelect multiple search={$searchPubkeys} value={filter['#p'] || []} onChange={change}>
<div slot="item" let:item let:context>
{#if context === "value"}
{displayPubkey(item)}
{:else}
<PersonBadge inert pubkey={item} />
{/if}
</div>
</SearchSelect>
</Field>

View File

@ -0,0 +1,20 @@
<script lang="ts">
import {omit} from 'ramda'
import Field from 'src/partials/Field.svelte'
import Input from 'src/partials/Input.svelte'
export let filter
export let onChange
export let onRemove
const change = e => onChange({...filter, search: e.target.value})
</script>
<Field>
<span slot="label" class="flex gap-2 items-center cursor-pointer" on:click={onRemove}>
<i class="fa fa-trash fa-sm" /> Search
</span>
<Input value={filter.search} on:change={change}>
<i slot="before" class="fa fa-search" />
</Input>
</Field>

View File

@ -0,0 +1,28 @@
<script lang="ts">
import {prop} from "ramda"
import {Scope} from '@coracle.social/feeds'
import {fuzzy} from 'src/util/misc'
import Field from "src/partials/Field.svelte"
import DateInput from "src/partials/DateInput.svelte"
import {searchPubkeys, displayPubkey} from 'src/engine'
export let filter
export let onChange
export let onRemove
const changeSince = since => onChange({...filter, since})
const changeUntil = until => onChange({...filter, until})
</script>
<div class="grid grid-cols-2 gap-2">
<Field>
<span slot="label" class="flex gap-2 items-center cursor-pointer" on:click={onRemove}>
<i class="fa fa-trash fa-sm" /> Since
</span>
<DateInput value={filter.since} onChange={changeSince} />
</Field>
<Field label="Until">
<DateInput value={filter.until} onChange={changeUntil} />
</Field>
</div>

View File

@ -0,0 +1,20 @@
<script lang="ts">
import {omit, prop} from "ramda"
import {fuzzy} from 'src/util/misc'
import Field from "src/partials/Field.svelte"
import SearchSelect from "src/partials/SearchSelect.svelte"
import {searchTopics} from 'src/engine'
export let filter
export let onChange
export let onRemove
const change = topics => onChange({...filter, '#t': topics})
</script>
<Field>
<span slot="label" class="flex gap-2 items-center cursor-pointer" on:click={onRemove}>
<i class="fa fa-trash fa-sm" /> Topics
</span>
<SearchSelect multiple search={$searchTopics} value={filter['#t'] || []} onChange={change} />
</Field>

View File

@ -12,7 +12,7 @@ import {
} from "@coracle.social/util"
import type {Feed, Loader} from "@coracle.social/feeds"
import {FeedLoader as CoreFeedLoader, FeedType} from "@coracle.social/feeds"
import {LOCAL_RELAY_URL, reactionKinds, repostKinds} from "src/util/nostr"
import {LOCAL_RELAY_URL, noteKinds, reactionKinds, repostKinds} from "src/util/nostr"
import type {DisplayEvent, Event} from "src/engine"
import {
feedLoader as baseFeedLoader,
@ -67,12 +67,17 @@ export class FeedLoader {
this.feedLoader = new CoreFeedLoader({
...baseFeedLoader.options,
request: async ({relays, filters, onEvent}) => {
// Default to note kinds
filters = filters.map(filter => ({kinds: noteKinds, ...filter}))
// Add reposts if we don't have any authors specified
if (this.opts.includeReposts && !filters.some(f => f.authors?.length > 0)) {
filters = addRepostFilters(filters)
}
const promises = []
// Use relays specified in feeds
if (relays.length > 0) {
promises.push(load({filters, relays, onEvent}))
} else {

View File

@ -2,7 +2,6 @@
import {onMount} from "svelte"
import {getIdOrAddress, decodeAddress} from "@coracle.social/util"
import {filterFeed} from "@coracle.social/feeds"
import {noteKinds} from "src/util/nostr"
import {fly} from "src/util/transition"
import FlexColumn from "src/partials/FlexColumn.svelte"
import Spinner from "src/partials/Spinner.svelte"
@ -39,7 +38,7 @@
hideControls
shouldListen
anchor={getIdOrAddress(event)}
feed={filterFeed({kinds: noteKinds, "#a": [address]})} />
feed={filterFeed({"#a": [address]})} />
</FlexColumn>
</div>
{:else}

View File

@ -2,7 +2,6 @@
import cx from "classnames"
import {Tags} from "@coracle.social/util"
import {Scope, filterFeed, relayFeed} from "@coracle.social/feeds"
import {noteKinds} from "src/util/nostr"
import {theme} from "src/partials/state"
import Anchor from "src/partials/Anchor.svelte"
import Popover from "src/partials/Popover.svelte"
@ -11,7 +10,7 @@
import {session, canSign, lists, userLists} from "src/engine"
export let relays = []
export let feed = filterFeed({kinds: noteKinds, scopes: [Scope.Follows]})
export let feed = filterFeed({scopes: [Scope.Follows]})
if (relays.length > 0) {
feed = relayFeed(relays, feed)
@ -31,11 +30,11 @@
const urls = tags.values("r").valueOf()
if (authors.length > 0) {
feed = filterFeed({kinds: noteKinds, authors})
feed = filterFeed({authors})
} else if (topics.length > 0) {
feed = filterFeed({kinds: noteKinds, "#t": topics})
feed = filterFeed({"#t": topics})
} else {
feed = filterFeed({kinds: noteKinds, scopes: [Scope.Follows]})
feed = filterFeed({scopes: [Scope.Follows]})
}
if (urls.length > 0) {

View File

@ -4,7 +4,6 @@
import {filterFeed} from "@coracle.social/feeds"
import {info} from "src/util/logger"
import {ensureProto} from "src/util/misc"
import {noteKinds} from "src/util/nostr"
import {themeBackgroundGradient} from "src/partials/state"
import Tabs from "src/partials/Tabs.svelte"
import Anchor from "src/partials/Anchor.svelte"
@ -31,7 +30,7 @@
export let npub
export let pubkey
export let relays = []
export let feed = filterFeed({kinds: noteKinds, authors: [pubkey]})
export let feed = filterFeed({authors: [pubkey]})
const tabs = ["notes", "likes", "collections", "relays"].filter(identity)
const person = derivePerson(pubkey)

View File

@ -1,7 +1,7 @@
<script lang="ts">
import {batch} from "hurdak"
import {filterFeed, relayFeed} from "@coracle.social/feeds"
import {getAvgRating, noteKinds} from "src/util/nostr"
import {getAvgRating} from "src/util/nostr"
import Feed from "src/app/shared/Feed.svelte"
import Tabs from "src/partials/Tabs.svelte"
import Rating from "src/partials/Rating.svelte"
@ -10,7 +10,7 @@
import {deriveRelay, normalizeRelayUrl, displayRelay, getMinWot} from "src/engine"
export let url
export let feed = filterFeed({kinds: noteKinds, min_wot: getMinWot()})
export let feed = filterFeed({min_wot: getMinWot()})
let reviews = []
let activeTab = "notes"

View File

@ -13,4 +13,4 @@
<TopicActions {topic} />
</div>
</div>
<Feed feed={filterFeed({kinds: [1], "#t": [topic]})} />
<Feed feed={filterFeed({"#t": [topic]})} />

View File

@ -1,6 +1,6 @@
import Fuse from "fuse.js"
import {doPipe} from "hurdak"
import {defaultTo, map, filter, sortBy} from "ramda"
import {defaultTo, pluck, map, filter, sortBy} from "ramda"
import {derived} from "@coracle.social/lib"
import {pubkey} from "src/engine/session/state"
import {user} from "src/engine/session/derived"
@ -53,3 +53,5 @@ export const searchPeople = derived(
}
},
)
export const searchPubkeys = searchPeople.derived(search => term => pluck("pubkey", search(term)))

View File

@ -1,13 +1,9 @@
<script lang="ts">
import cx from "classnames"
import {createEventDispatcher} from 'svelte'
export let wrapperClass = ""
export let value
const dispatch = createEventDispatcher()
const onChange = e => dispatch('change', e.target.value)
export let onChange = null
export let wrapperClass = ""
const className = cx(
$$props.class,
@ -17,7 +13,7 @@
</script>
<div class={cx(wrapperClass, "relative")}>
<select {...$$props} class={className} bind:value on:change={onChange}>
<select {...$$props} class={className} bind:value on:change={() => onChange(value)}>
<slot />
</select>
<div class="absolute left-0 top-0 flex gap-2 px-3 pt-3 text-tinted-700">

View File

@ -1,10 +1,12 @@
<script lang="ts">
import cx from "classnames"
import {without} from 'ramda'
export let options
export let value
export let onChange = null
export let disabled = false
export let multiple = false
export let displayOption = x => x
</script>
@ -19,10 +21,15 @@
<div
class={cx("px-4 py-2 transition-all", {
"border-l border-solid border-neutral-100": i > 0,
"bg-accent text-white": value === option,
"bg-accent text-white": multiple ? value.includes(option) : value === option,
})}
on:click={() => {
value = option
if (multiple) {
value = value.includes(option) ? without([option], value) : [...value, option]
} else {
value = option
}
onChange?.(value)
}}>
{displayOption(option)}

View File

@ -25,7 +25,7 @@ export const isKeyValid = (key: string) => {
return true
}
export const noteKinds = [1, 30023, 9802, 1808, 32123, 31923, 30402]
export const noteKinds = [1, 30023, 9802, 1808, 31337, 31923, 30402]
export const reactionKinds = [7, 9735]
export const repostKinds = [6, 16]
export const giftWrapKinds = [1059, 1060]