mirror of
https://github.com/coracle-social/coracle.git
synced 2024-09-20 20:16:43 +00:00
Split up search and scan
This commit is contained in:
parent
b07c06e952
commit
625c1db27c
@ -38,6 +38,7 @@
|
|||||||
|
|
||||||
# More
|
# More
|
||||||
|
|
||||||
|
- [ ] Separate mentions from other notifications
|
||||||
- [ ] Performance
|
- [ ] Performance
|
||||||
- same deal as with formatTimestamp, don't use functions inline since svelte can't cache them
|
- same deal as with formatTimestamp, don't use functions inline since svelte can't cache them
|
||||||
- [ ] Image Uploads
|
- [ ] Image Uploads
|
||||||
|
@ -43,6 +43,7 @@
|
|||||||
import NotFound from "src/routes/NotFound.svelte"
|
import NotFound from "src/routes/NotFound.svelte"
|
||||||
import PersonDetail from "src/routes/PersonDetail.svelte"
|
import PersonDetail from "src/routes/PersonDetail.svelte"
|
||||||
import Search from "src/routes/Search.svelte"
|
import Search from "src/routes/Search.svelte"
|
||||||
|
import Scan from "src/routes/Scan.svelte"
|
||||||
import RelayDetail from "src/routes/RelayDetail.svelte"
|
import RelayDetail from "src/routes/RelayDetail.svelte"
|
||||||
import RelayList from "src/routes/RelayList.svelte"
|
import RelayList from "src/routes/RelayList.svelte"
|
||||||
import Profile from "src/views/Profile.svelte"
|
import Profile from "src/views/Profile.svelte"
|
||||||
@ -177,9 +178,14 @@
|
|||||||
{#if $ready}
|
{#if $ready}
|
||||||
<div class="pt-16 text-white h-full lg:ml-56">
|
<div class="pt-16 text-white h-full lg:ml-56">
|
||||||
<Route path="/alerts" component={Alerts} />
|
<Route path="/alerts" component={Alerts} />
|
||||||
<Route path="/search/:activeTab" let:params>
|
<Route path="/search">
|
||||||
<EnsureData enforcePeople={false}>
|
<EnsureData enforcePeople={false}>
|
||||||
<Search activeTab={params.activeTab} />
|
<Search />
|
||||||
|
</EnsureData>
|
||||||
|
</Route>
|
||||||
|
<Route path="/scan">
|
||||||
|
<EnsureData enforcePeople={false}>
|
||||||
|
<Scan />
|
||||||
</EnsureData>
|
</EnsureData>
|
||||||
</Route>
|
</Route>
|
||||||
<Route path="/notes/:activeTab" let:params>
|
<Route path="/notes/:activeTab" let:params>
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import {uniq, pick, identity, isEmpty} from 'ramda'
|
import {uniq, pick, identity, isEmpty} from 'ramda'
|
||||||
import {nip05} from 'nostr-tools'
|
import {nip05} from 'nostr-tools'
|
||||||
import {noop, createMap, ensurePlural, chunk, switcherFn} from 'hurdak/lib/hurdak'
|
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 {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 {Tags, roomAttrs, personKinds, isRelay, isShareableRelay, normalizeRelayUrl} from 'src/util/nostr'
|
||||||
import database from 'src/agent/database'
|
import database from 'src/agent/database'
|
||||||
@ -307,7 +307,7 @@ const verifyNip05 = (pubkey, as) =>
|
|||||||
}, noop)
|
}, noop)
|
||||||
|
|
||||||
const verifyZapper = async (pubkey, address) => {
|
const verifyZapper = async (pubkey, address) => {
|
||||||
let url, zapper, lnurl
|
let url
|
||||||
|
|
||||||
// Try to parse it as a lud06 LNURL or as a lud16 address
|
// Try to parse it as a lud06 LNURL or as a lud16 address
|
||||||
if (address.startsWith('lnurl1')) {
|
if (address.startsWith('lnurl1')) {
|
||||||
@ -325,13 +325,8 @@ const verifyZapper = async (pubkey, address) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const res = await tryFetch(() => fetch(url))
|
const res = await tryFetch(() => fetch(url))
|
||||||
|
const zapper = await tryJson(() => res.json())
|
||||||
try {
|
const lnurl = lnurlEncode('lnurl', url)
|
||||||
zapper = await res.json()
|
|
||||||
lnurl = lnurlEncode('lnurl', url)
|
|
||||||
} catch (e) {
|
|
||||||
warn(e)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (zapper?.allowsNostr && zapper?.nostrPubkey) {
|
if (zapper?.allowsNostr && zapper?.nostrPubkey) {
|
||||||
database.people.patch({pubkey, zapper, lnurl})
|
database.people.patch({pubkey, zapper, lnurl})
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
import PersonInfo from "src/views/person/PersonInfo.svelte"
|
import PersonInfo from "src/views/person/PersonInfo.svelte"
|
||||||
import RelaySearch from "src/views/relays/RelaySearch.svelte"
|
import RelaySearch from "src/views/relays/RelaySearch.svelte"
|
||||||
import RelayCard from "src/views/relays/RelayCard.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 database from 'src/agent/database'
|
||||||
import user from 'src/agent/user'
|
import user from 'src/agent/user'
|
||||||
|
|
||||||
@ -76,8 +76,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/each}
|
{/each}
|
||||||
<SearchPeople hideFollowing />
|
|
||||||
</Content>
|
</Content>
|
||||||
|
<Search hideFollowing />
|
||||||
</Modal>
|
</Modal>
|
||||||
{:else if needsPeople()}
|
{:else if needsPeople()}
|
||||||
<Content size="lg">
|
<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>
|
<script>
|
||||||
import {navigate} from 'svelte-routing'
|
import {fuzzy} from "src/util/misc"
|
||||||
import Content from 'src/partials/Content.svelte'
|
import {personKinds} from "src/util/nostr"
|
||||||
import Tabs from 'src/partials/Tabs.svelte'
|
import Input from "src/partials/Input.svelte"
|
||||||
import SearchPeople from 'src/views/search/SearchPeople.svelte'
|
import Spinner from "src/partials/Spinner.svelte"
|
||||||
import Scan from 'src/views/search/Scan.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"
|
document.title = "Search"
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Content>
|
<Content>
|
||||||
<Tabs tabs={['people', 'advanced']} {activeTab} {setActiveTab} />
|
<Input bind:value={q} placeholder="Search for people">
|
||||||
{#if activeTab === 'people'}
|
<i slot="before" class="fa-solid fa-search" />
|
||||||
<SearchPeople />
|
</Input>
|
||||||
{:else if activeTab === 'advanced'}
|
{#each (search ? search(q) : []).slice(0, 50) as person (person.pubkey)}
|
||||||
<Scan />
|
{#if person.pubkey !== user.getPubkey() && !(hideFollowing && $petnamePubkeys.includes(person.pubkey))}
|
||||||
|
<PersonInfo {person} />
|
||||||
{/if}
|
{/if}
|
||||||
|
{:else}
|
||||||
|
<Spinner />
|
||||||
|
{/each}
|
||||||
</Content>
|
</Content>
|
||||||
|
@ -46,10 +46,15 @@
|
|||||||
</li>
|
</li>
|
||||||
{/if}
|
{/if}
|
||||||
<li class="cursor-pointer">
|
<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
|
<i class="fa fa-search mr-2" /> Search
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</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">
|
<li class="cursor-pointer">
|
||||||
<a class="block px-4 py-2 hover:bg-accent transition-all" href="/notes/follows">
|
<a class="block px-4 py-2 hover:bg-accent transition-all" href="/notes/follows">
|
||||||
<i class="fa fa-rss mr-2" /> Feed
|
<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