mirror of
https://github.com/coracle-social/coracle.git
synced 2024-09-18 19:23:40 +00:00
Add topic search, tweak topic autocomplete
This commit is contained in:
parent
8573140134
commit
6d4a9c8d0d
@ -1,8 +1,10 @@
|
||||
# Current
|
||||
|
||||
- [ ] Buttons on profile detail is broken
|
||||
- [ ] Topics
|
||||
- [ ] Improve topic suggestions and rendering
|
||||
- [ ] Add topic search, keep cache of topics
|
||||
- [x] Improve topic suggestions and rendering
|
||||
- [x] Add topic search, keep cache of topics
|
||||
- [ ] Add ability to follow topics
|
||||
- [ ] Relays bounty
|
||||
- [ ] Ability to create custom feeds
|
||||
- [ ] Add global/following/network tabs to relay detail
|
||||
@ -69,7 +71,7 @@
|
||||
- [ ] Review QR codes, search, basic affordances for link navigation
|
||||
- [ ] Add delete button to notes
|
||||
- [ ] Log in as user button
|
||||
- [ ] Separate notifications out by type, mute certain kinds
|
||||
- [ ] Separate notifications out by type, mute certain kinds. Likes are extraneous
|
||||
- [ ] Relay recommendations based on follows/followers
|
||||
- [ ] Make the note relays button modal make sense, one relay with no explanation is not good
|
||||
- [ ] Linkify invoices
|
||||
|
@ -6,6 +6,7 @@ import {throttle} from "throttle-debounce"
|
||||
import {writable} from "svelte/store"
|
||||
import {ensurePlural, noop, createMap} from "hurdak/lib/hurdak"
|
||||
import {Tags} from "src/util/nostr"
|
||||
import {fuzzy} from "src/util/misc"
|
||||
import user from "src/agent/user"
|
||||
|
||||
const Adapter = window.indexedDB ? IncrementalIndexedDBAdapter : Loki.LokiMemoryAdapter
|
||||
@ -224,3 +225,9 @@ export const onReady = cb => {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export const searchPeople = watch("people", t =>
|
||||
fuzzy(t.all({"kind0.name": {$type: "string"}}), {
|
||||
keys: ["kind0.name", "kind0.about", "pubkey"],
|
||||
})
|
||||
)
|
||||
|
@ -344,7 +344,7 @@ addHandler(10002, e => {
|
||||
// Topics
|
||||
|
||||
const processTopics = e => {
|
||||
const matches = Array.from(e.content.matchAll(/#(\w{2,100})/g))
|
||||
const matches = Array.from(e.content.toLowerCase().matchAll(/#(\w{2,100})/g))
|
||||
|
||||
if (matches.length > 0) {
|
||||
topics.patch(matches.map(nth(1)).map(objOf("name")))
|
||||
|
@ -72,7 +72,7 @@
|
||||
<div>Search below to find some interesting people.</div>
|
||||
</div>
|
||||
{/each}
|
||||
<PersonSearch hideFollowing />
|
||||
<PersonSearch />
|
||||
</Content>
|
||||
</Modal>
|
||||
{:else if needsPeople()}
|
||||
|
@ -122,7 +122,7 @@
|
||||
<br />
|
||||
{/each}
|
||||
{:else if type === "topic"}
|
||||
<Anchor on:click={() => openTopic(value)}>{value}</Anchor>
|
||||
<Anchor on:click={() => openTopic(value)}>#{value}</Anchor>
|
||||
{:else if type === "link"}
|
||||
<Anchor external href={value}>
|
||||
{value.replace(/https?:\/\/(www\.)?/, "")}
|
||||
|
@ -1,42 +1,28 @@
|
||||
<script>
|
||||
import {fuzzy} from "src/util/misc"
|
||||
import {personKinds} from "src/util/nostr"
|
||||
import Input from "src/partials/Input.svelte"
|
||||
import Spinner from "src/partials/Spinner.svelte"
|
||||
import PersonInfo from "src/app/shared/PersonInfo.svelte"
|
||||
import {getUserReadRelays} from "src/agent/relays"
|
||||
import {watch} from "src/agent/db"
|
||||
import {searchPeople} from "src/agent/db"
|
||||
import network from "src/agent/network"
|
||||
import user from "src/agent/user"
|
||||
|
||||
export let hideFollowing = false
|
||||
|
||||
let q
|
||||
let results = []
|
||||
|
||||
const {petnamePubkeys} = user
|
||||
const search = watch("people", t =>
|
||||
fuzzy(t.all({"kind0.name": {$type: "string"}}), {
|
||||
keys: ["kind0.name", "kind0.about", "pubkey"],
|
||||
})
|
||||
)
|
||||
|
||||
$: results = $search(q).slice(0, 50)
|
||||
$: results = $searchPeople(q).slice(0, 50)
|
||||
|
||||
// Prime our database, in case we don't have any people stored yet
|
||||
network.load({
|
||||
relays: getUserReadRelays(),
|
||||
filter: {kinds: personKinds, limit: 10},
|
||||
filter: {kinds: [0], limit: 10},
|
||||
})
|
||||
|
||||
document.title = "Search"
|
||||
</script>
|
||||
|
||||
<Input bind:value={q} placeholder="Search for people">
|
||||
<i slot="before" class="fa-solid fa-search" />
|
||||
</Input>
|
||||
{#each results as person (person.pubkey)}
|
||||
{#if person.pubkey !== user.getPubkey() && !(hideFollowing && $petnamePubkeys.includes(person.pubkey))}
|
||||
{#if person.pubkey !== user.getPubkey()}
|
||||
<PersonInfo {person} />
|
||||
{/if}
|
||||
{:else}
|
||||
|
@ -1,16 +1,62 @@
|
||||
<script>
|
||||
import {identity, sortBy, prop} from "ramda"
|
||||
import {fuzzy} from "src/util/misc"
|
||||
import {modal} from "src/partials/state"
|
||||
import Input from "src/partials/Input.svelte"
|
||||
import Heading from "src/partials/Heading.svelte"
|
||||
import Content from "src/partials/Content.svelte"
|
||||
import PersonSearch from "src/app/shared/PersonSearch.svelte"
|
||||
import Anchor from "src/partials/Anchor.svelte"
|
||||
import PersonInfo from "src/app/shared/PersonInfo.svelte"
|
||||
import {watch} from "src/agent/db"
|
||||
import user from "src/agent/user"
|
||||
|
||||
let q
|
||||
|
||||
$: search = watch(["people", "topics"], (p, t) => {
|
||||
const topics = t
|
||||
.all()
|
||||
.map(topic => ({type: "topic", id: topic.name, topic, text: "#" + topic.name}))
|
||||
const people = p
|
||||
.all({"kind0.name": {$type: "string"}, pubkey: {$ne: user.getPubkey()}})
|
||||
.map(person => ({
|
||||
person,
|
||||
type: "person",
|
||||
id: person.pubkey,
|
||||
text: "@" + [person.kind0.name, person.kind0.about].filter(identity).join(" "),
|
||||
}))
|
||||
|
||||
return fuzzy(sortBy(prop("id"), topics.concat(people)), {keys: ["text"]})
|
||||
})
|
||||
|
||||
const openTopic = topic => {
|
||||
modal.push({type: "topic/feed", topic})
|
||||
}
|
||||
|
||||
document.title = "Search"
|
||||
</script>
|
||||
|
||||
<Content>
|
||||
<div class="flex flex-col items-center justify-center">
|
||||
<Heading>Profile Search</Heading>
|
||||
<Heading>Search</Heading>
|
||||
<p>
|
||||
Search for people on Nostr. For now, only profiles that have already been loaded will appear
|
||||
in search results.
|
||||
Search for people and topics on Nostr. For now, only results that have already been loaded
|
||||
will appear in search results.
|
||||
</p>
|
||||
</div>
|
||||
<PersonSearch />
|
||||
<Input bind:value={q} placeholder="Search for people or topics">
|
||||
<i slot="before" class="fa-solid fa-search" />
|
||||
</Input>
|
||||
{#each $search(q).slice(0, 50) as result (result.type + result.id)}
|
||||
{#if result.type === "topic"}
|
||||
<Anchor
|
||||
type="unstyled"
|
||||
class="flex gap-4 border-l-2 border-solid border-gray-7 py-2 px-4 transition-all
|
||||
hover:border-accent hover:bg-gray-8"
|
||||
on:click={() => openTopic(result.topic.name)}>
|
||||
#{result.topic.name}
|
||||
</Anchor>
|
||||
{:else if result.type === "person"}
|
||||
<PersonInfo person={result.person} />
|
||||
{/if}
|
||||
{/each}
|
||||
</Content>
|
||||
|
@ -6,10 +6,10 @@
|
||||
|
||||
export let topic
|
||||
const relays = sampleRelays(getUserReadRelays())
|
||||
const filter = [{kinds: [1], "#t": [topic.replace("#", "")]}]
|
||||
const filter = [{kinds: [1], "#t": [topic]}]
|
||||
</script>
|
||||
|
||||
<Content>
|
||||
<Heading class="text-center">{topic}</Heading>
|
||||
<Heading class="text-center">#{topic}</Heading>
|
||||
<Feed {relays} {filter} />
|
||||
</Content>
|
||||
|
@ -44,6 +44,8 @@
|
||||
}
|
||||
|
||||
const autocomplete = ({person = null, force = false} = {}) => {
|
||||
let completed = false
|
||||
|
||||
const {selection, node, offset, word} = getInfo()
|
||||
|
||||
const annotate = (prefix, text, value) => {
|
||||
@ -67,10 +69,12 @@
|
||||
selection.collapse(span.nextSibling, 0)
|
||||
selection.getRangeAt(0).insertNode(space)
|
||||
selection.collapse(space, 2)
|
||||
|
||||
completed = true
|
||||
}
|
||||
|
||||
// Mentions
|
||||
if ((force || word.length > 1) && word.startsWith("@") && person) {
|
||||
if ((force || word.length > 1) && word.startsWith("@")) {
|
||||
annotate("@", displayPerson(person).trim(), pubkeyEncoder.encode(person.pubkey))
|
||||
}
|
||||
|
||||
@ -80,6 +84,8 @@
|
||||
}
|
||||
|
||||
suggestions.setData([])
|
||||
|
||||
return completed
|
||||
}
|
||||
|
||||
const onKeyDown = e => {
|
||||
@ -105,7 +111,9 @@
|
||||
|
||||
// Only autocomplete topics on space
|
||||
if (["Space"].includes(e.code)) {
|
||||
autocomplete()
|
||||
if (autocomplete()) {
|
||||
e.preventDefault()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -193,7 +193,7 @@ export const parseContent = ({content, tags = []}) => {
|
||||
const topic = first(text.match(/^#\w+/i))
|
||||
|
||||
if (topic) {
|
||||
return ["topic", topic, topic]
|
||||
return ["topic", topic, topic.slice(1)]
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user