mirror of
https://github.com/coracle-social/coracle.git
synced 2024-09-16 02:03:36 +00:00
Split up search and scan
This commit is contained in:
parent
b07c06e952
commit
625c1db27c
@ -38,6 +38,7 @@
|
||||
|
||||
# More
|
||||
|
||||
- [ ] Separate mentions from other notifications
|
||||
- [ ] Performance
|
||||
- same deal as with formatTimestamp, don't use functions inline since svelte can't cache them
|
||||
- [ ] Image Uploads
|
||||
|
@ -43,6 +43,7 @@
|
||||
import NotFound from "src/routes/NotFound.svelte"
|
||||
import PersonDetail from "src/routes/PersonDetail.svelte"
|
||||
import Search from "src/routes/Search.svelte"
|
||||
import Scan from "src/routes/Scan.svelte"
|
||||
import RelayDetail from "src/routes/RelayDetail.svelte"
|
||||
import RelayList from "src/routes/RelayList.svelte"
|
||||
import Profile from "src/views/Profile.svelte"
|
||||
@ -177,9 +178,14 @@
|
||||
{#if $ready}
|
||||
<div class="pt-16 text-white h-full lg:ml-56">
|
||||
<Route path="/alerts" component={Alerts} />
|
||||
<Route path="/search/:activeTab" let:params>
|
||||
<Route path="/search">
|
||||
<EnsureData enforcePeople={false}>
|
||||
<Search activeTab={params.activeTab} />
|
||||
<Search />
|
||||
</EnsureData>
|
||||
</Route>
|
||||
<Route path="/scan">
|
||||
<EnsureData enforcePeople={false}>
|
||||
<Scan />
|
||||
</EnsureData>
|
||||
</Route>
|
||||
<Route path="/notes/:activeTab" let:params>
|
||||
|
@ -1,7 +1,7 @@
|
||||
import {uniq, pick, identity, isEmpty} from 'ramda'
|
||||
import {nip05} from 'nostr-tools'
|
||||
import {noop, createMap, ensurePlural, chunk, switcherFn} from 'hurdak/lib/hurdak'
|
||||
import {log, warn} from 'src/util/logger'
|
||||
import {log} from 'src/util/logger'
|
||||
import {lnurlEncode, lnurlDecode, tryFetch, now, sleep, tryJson, timedelta, shuffle, hash} from 'src/util/misc'
|
||||
import {Tags, roomAttrs, personKinds, isRelay, isShareableRelay, normalizeRelayUrl} from 'src/util/nostr'
|
||||
import database from 'src/agent/database'
|
||||
@ -307,7 +307,7 @@ const verifyNip05 = (pubkey, as) =>
|
||||
}, noop)
|
||||
|
||||
const verifyZapper = async (pubkey, address) => {
|
||||
let url, zapper, lnurl
|
||||
let url
|
||||
|
||||
// Try to parse it as a lud06 LNURL or as a lud16 address
|
||||
if (address.startsWith('lnurl1')) {
|
||||
@ -325,13 +325,8 @@ const verifyZapper = async (pubkey, address) => {
|
||||
}
|
||||
|
||||
const res = await tryFetch(() => fetch(url))
|
||||
|
||||
try {
|
||||
zapper = await res.json()
|
||||
lnurl = lnurlEncode('lnurl', url)
|
||||
} catch (e) {
|
||||
warn(e)
|
||||
}
|
||||
const zapper = await tryJson(() => res.json())
|
||||
const lnurl = lnurlEncode('lnurl', url)
|
||||
|
||||
if (zapper?.allowsNostr && zapper?.nostrPubkey) {
|
||||
database.people.patch({pubkey, zapper, lnurl})
|
||||
|
@ -5,7 +5,7 @@
|
||||
import PersonInfo from "src/views/person/PersonInfo.svelte"
|
||||
import RelaySearch from "src/views/relays/RelaySearch.svelte"
|
||||
import RelayCard from "src/views/relays/RelayCard.svelte"
|
||||
import SearchPeople from "src/views/search/SearchPeople.svelte"
|
||||
import Search from "src/routes/Search.svelte"
|
||||
import database from 'src/agent/database'
|
||||
import user from 'src/agent/user'
|
||||
|
||||
@ -76,8 +76,8 @@
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
<SearchPeople hideFollowing />
|
||||
</Content>
|
||||
<Search hideFollowing />
|
||||
</Modal>
|
||||
{:else if needsPeople()}
|
||||
<Content size="lg">
|
||||
|
81
src/routes/Scan.svelte
Normal file
81
src/routes/Scan.svelte
Normal file
@ -0,0 +1,81 @@
|
||||
<script lang="ts">
|
||||
import QrScanner from 'qr-scanner'
|
||||
import {onDestroy} from 'svelte'
|
||||
import {navigate} from 'svelte-routing'
|
||||
import {find} from 'ramda'
|
||||
import {nip05, nip19} from 'nostr-tools'
|
||||
import Input from 'src/partials/Input.svelte'
|
||||
import Anchor from 'src/partials/Anchor.svelte'
|
||||
import Spinner from 'src/partials/Spinner.svelte'
|
||||
import Content from "src/partials/Content.svelte"
|
||||
import {toast} from "src/app/ui"
|
||||
|
||||
let video, value, scanner, status = ''
|
||||
|
||||
const onDecode = result => {
|
||||
handleInput(result.data)
|
||||
}
|
||||
|
||||
const handleInput = async input => {
|
||||
input = input.replace('nostr:', '')
|
||||
|
||||
if (find(s => input.startsWith(s), ["note1", "npub1", "nevent1", "nprofile1"])) {
|
||||
navigate("/" + input)
|
||||
return
|
||||
}
|
||||
|
||||
if (input.match(/^[a-f0-9]{64}$/)) {
|
||||
navigate("/" + nip19.npubEncode(input))
|
||||
return
|
||||
}
|
||||
|
||||
let profile = await nip05.queryProfile(input)
|
||||
if (profile) {
|
||||
navigate("/" + nip19.nprofileEncode(profile))
|
||||
return
|
||||
}
|
||||
|
||||
toast.show("warning", "That isn't a valid nostr identifier")
|
||||
}
|
||||
|
||||
const showVideo = async () => {
|
||||
status = 'loading'
|
||||
|
||||
scanner = new QrScanner(video, onDecode, {
|
||||
returnDetailedScanResult: true,
|
||||
})
|
||||
|
||||
await scanner.start()
|
||||
|
||||
status = 'ready'
|
||||
}
|
||||
|
||||
onDestroy(async () => {
|
||||
if (scanner) {
|
||||
await scanner.stop()
|
||||
await scanner.destroy()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<Content>
|
||||
<form class="flex gap-2" on:submit|preventDefault={() => handleInput(value)}>
|
||||
<Input placeholder="nprofile..." bind:value={value} wrapperClass="flex-grow" />
|
||||
<Anchor type="button" on:click={() => handleInput(value)}>
|
||||
<i class="fa fa-arrow-right" />
|
||||
</Anchor>
|
||||
<Anchor type="button" on:click={showVideo}>
|
||||
<i class="fa fa-camera" />
|
||||
</Anchor>
|
||||
</form>
|
||||
<div class="text-center text-light">
|
||||
Enter any nostr identifier (npub, nevent, nprofile, note or user@domain.tld), or click on the
|
||||
camera icon to scan with your device's camera instead.
|
||||
</div>
|
||||
{#if status === 'loading'}
|
||||
<Spinner>
|
||||
Loading your camera...
|
||||
</Spinner>
|
||||
{/if}
|
||||
<video bind:this={video} />
|
||||
</Content>
|
@ -1,22 +1,47 @@
|
||||
<script>
|
||||
import {navigate} from 'svelte-routing'
|
||||
import Content from 'src/partials/Content.svelte'
|
||||
import Tabs from 'src/partials/Tabs.svelte'
|
||||
import SearchPeople from 'src/views/search/SearchPeople.svelte'
|
||||
import Scan from 'src/views/search/Scan.svelte'
|
||||
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 Content from "src/partials/Content.svelte"
|
||||
import PersonInfo from 'src/views/person/PersonInfo.svelte'
|
||||
import {getUserReadRelays} from 'src/agent/relays'
|
||||
import database from 'src/agent/database'
|
||||
import network from 'src/agent/network'
|
||||
import user from 'src/agent/user'
|
||||
|
||||
export let activeTab
|
||||
export let hideFollowing = false
|
||||
|
||||
const setActiveTab = tab => navigate(`/search/${tab}`)
|
||||
let q
|
||||
let search
|
||||
|
||||
const {petnamePubkeys} = user
|
||||
|
||||
database.watch('people', table => {
|
||||
search = fuzzy(
|
||||
table.all({'kind0.name:!nil': null}),
|
||||
{keys: ["kind0.name", "kind0.about", "pubkey"]}
|
||||
)
|
||||
})
|
||||
|
||||
// Prime our database, in case we don't have any people stored yet
|
||||
network.load({
|
||||
relays: getUserReadRelays(),
|
||||
filter: {kinds: personKinds, limit: 10},
|
||||
})
|
||||
|
||||
document.title = "Search"
|
||||
</script>
|
||||
|
||||
<Content>
|
||||
<Tabs tabs={['people', 'advanced']} {activeTab} {setActiveTab} />
|
||||
{#if activeTab === 'people'}
|
||||
<SearchPeople />
|
||||
{:else if activeTab === 'advanced'}
|
||||
<Scan />
|
||||
{/if}
|
||||
<Input bind:value={q} placeholder="Search for people">
|
||||
<i slot="before" class="fa-solid fa-search" />
|
||||
</Input>
|
||||
{#each (search ? search(q) : []).slice(0, 50) as person (person.pubkey)}
|
||||
{#if person.pubkey !== user.getPubkey() && !(hideFollowing && $petnamePubkeys.includes(person.pubkey))}
|
||||
<PersonInfo {person} />
|
||||
{/if}
|
||||
{:else}
|
||||
<Spinner />
|
||||
{/each}
|
||||
</Content>
|
||||
|
@ -46,10 +46,15 @@
|
||||
</li>
|
||||
{/if}
|
||||
<li class="cursor-pointer">
|
||||
<a class="block px-4 py-2 hover:bg-accent transition-all" href="/search/people">
|
||||
<a class="block px-4 py-2 hover:bg-accent transition-all" href="/search">
|
||||
<i class="fa fa-search mr-2" /> Search
|
||||
</a>
|
||||
</li>
|
||||
<li class="cursor-pointer">
|
||||
<a class="block px-4 py-2 hover:bg-accent transition-all" href="/scan">
|
||||
<i class="fa fa-qrcode mr-2" /> Scan
|
||||
</a>
|
||||
</li>
|
||||
<li class="cursor-pointer">
|
||||
<a class="block px-4 py-2 hover:bg-accent transition-all" href="/notes/follows">
|
||||
<i class="fa fa-rss mr-2" /> Feed
|
||||
|
@ -1,97 +0,0 @@
|
||||
<script lang="ts">
|
||||
import QrScanner from 'qr-scanner'
|
||||
import {onDestroy} from 'svelte'
|
||||
import {navigate} from 'svelte-routing'
|
||||
import {waitFor} from 'hurdak/lib/hurdak'
|
||||
import {find} from 'ramda'
|
||||
import {nip05, nip19} from 'nostr-tools'
|
||||
import Input from 'src/partials/Input.svelte'
|
||||
import Anchor from 'src/partials/Anchor.svelte'
|
||||
import Spinner from 'src/partials/Spinner.svelte'
|
||||
import {toast} from "src/app/ui"
|
||||
|
||||
let mode = 'input', video, ready, value, scanner
|
||||
|
||||
const onDecode = result => {
|
||||
handleInput(result.data)
|
||||
}
|
||||
|
||||
const handleInput = async input => {
|
||||
input = input.replace('nostr:', '')
|
||||
|
||||
if (find(s => input.startsWith(s), ["note1", "npub1", "nevent1", "nprofile1"])) {
|
||||
navigate("/" + input)
|
||||
return
|
||||
}
|
||||
|
||||
if (input.match(/^[a-f0-9]{64}$/)) {
|
||||
navigate("/" + nip19.npubEncode(input))
|
||||
return
|
||||
}
|
||||
|
||||
let profile = await nip05.queryProfile(input)
|
||||
if (profile) {
|
||||
navigate("/" + nip19.nprofileEncode(profile))
|
||||
return
|
||||
}
|
||||
|
||||
toast.show("warning", "That isn't a valid nostr identifier")
|
||||
return
|
||||
}
|
||||
|
||||
const setMode = async newMode => {
|
||||
mode = newMode
|
||||
|
||||
if (newMode === 'scan') {
|
||||
await waitFor(() => video)
|
||||
|
||||
scanner = new QrScanner(video, onDecode, {
|
||||
returnDetailedScanResult: true,
|
||||
})
|
||||
|
||||
await scanner.start()
|
||||
|
||||
ready = true
|
||||
} else if (scanner) {
|
||||
ready = false
|
||||
await scanner.stop()
|
||||
await scanner.destroy()
|
||||
}
|
||||
}
|
||||
|
||||
onDestroy(async () => {
|
||||
if (scanner) {
|
||||
await scanner.stop()
|
||||
await scanner.destroy()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
{#if mode === 'input'}
|
||||
<form class="flex gap-2" on:submit|preventDefault={() => handleInput(value)}>
|
||||
<Input placeholder="nprofile..." bind:value={value} wrapperClass="flex-grow" />
|
||||
<Anchor type="button" on:click={() => handleInput(value)}>
|
||||
<i class="fa fa-arrow-right" />
|
||||
</Anchor>
|
||||
<Anchor type="button" on:click={() => setMode('scan')}>
|
||||
<i class="fa fa-qrcode" />
|
||||
</Anchor>
|
||||
</form>
|
||||
<div class="text-center text-light">
|
||||
Enter any nostr identifier (npub, nevent, nprofile, note or user@domain.tld), or click on the
|
||||
camera icon to scan with your device's camera instead.
|
||||
</div>
|
||||
{:else}
|
||||
{#if !ready}
|
||||
<Spinner>
|
||||
Loading your camera...
|
||||
</Spinner>
|
||||
{/if}
|
||||
<video bind:this={video} />
|
||||
{#if ready}
|
||||
<Anchor type="unstyled" class="flex gap-2 items-center" on:click={() => setMode('input')}>
|
||||
<i class="fa fa-arrow-left" />
|
||||
<span class="underline">Go back</span>
|
||||
</Anchor>
|
||||
{/if}
|
||||
{/if}
|
@ -1,43 +0,0 @@
|
||||
<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/views/person/PersonInfo.svelte'
|
||||
import {getUserReadRelays} from 'src/agent/relays'
|
||||
import database from 'src/agent/database'
|
||||
import network from 'src/agent/network'
|
||||
import user from 'src/agent/user'
|
||||
|
||||
export let hideFollowing = false
|
||||
|
||||
let q
|
||||
let search
|
||||
|
||||
const {petnamePubkeys} = user
|
||||
|
||||
database.watch('people', table => {
|
||||
search = fuzzy(
|
||||
table.all({'kind0.name:!nil': null}),
|
||||
{keys: ["kind0.name", "kind0.about", "pubkey"]}
|
||||
)
|
||||
})
|
||||
|
||||
// Prime our database, in case we don't have any people stored yet
|
||||
network.load({
|
||||
relays: getUserReadRelays(),
|
||||
filter: {kinds: personKinds, limit: 10},
|
||||
})
|
||||
</script>
|
||||
|
||||
<Input bind:value={q} placeholder="Search for people">
|
||||
<i slot="before" class="fa-solid fa-search" />
|
||||
</Input>
|
||||
|
||||
{#each (search ? search(q) : []).slice(0, 50) as person (person.pubkey)}
|
||||
{#if person.pubkey !== user.getPubkey() && !(hideFollowing && $petnamePubkeys.includes(person.pubkey))}
|
||||
<PersonInfo {person} />
|
||||
{/if}
|
||||
{:else}
|
||||
<Spinner />
|
||||
{/each}
|
Loading…
Reference in New Issue
Block a user