Split up search and scan

This commit is contained in:
Jonathan Staab 2023-03-07 13:12:26 -06:00
parent b07c06e952
commit 625c1db27c
9 changed files with 140 additions and 167 deletions

View File

@ -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

View File

@ -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>

View File

@ -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})

View File

@ -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
View 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>

View File

@ -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>

View File

@ -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

View File

@ -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}

View File

@ -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}