Consolidate RelayCard

This commit is contained in:
Jonathan Staab 2023-04-10 14:18:35 -05:00
parent 260328b757
commit 08d6195880
8 changed files with 148 additions and 196 deletions

View File

@ -7,6 +7,7 @@
- [ ] Relays bounty
- [ ] Ability to create custom feeds
- [ ] Add global/following/network tabs to relay detail
- [ ] Fix tag-style event mentions. Probably transform all mentions into entities in parse
- [ ] Some lnurls aren't working npub1y3k2nheva29y9ej8a22e07epuxrn04rvgy28wvs54y57j7vsxxuq0gvp4j
- [ ] Fix performance issues

View File

@ -1,57 +1,108 @@
<script lang="ts">
import {find, propEq} from "ramda"
import cx from "classnames"
import {find, last, propEq} from "ramda"
import {between} from "hurdak/lib/hurdak"
import {onMount} from "svelte"
import {poll} from "src/util/misc"
import {fly} from "svelte/transition"
import {poll, stringToHue, hsl} from "src/util/misc"
import Toggle from "src/partials/Toggle.svelte"
import RelayCard from "src/partials/RelayCard.svelte"
import Anchor from "src/partials/Anchor.svelte"
import pool from "src/agent/pool"
import user from "src/agent/user"
import keys from "src/agent/keys"
import {loadAppData} from "src/app"
export let relay
export let theme = "gray-8"
export let showStatus = false
export let showActions = false
export let showControls = false
const {relays, canPublish} = user
let statusHover = false
let quality = null
let message = null
let joined = false
const {relays, canPublish} = user
const {method} = keys
const isPubkeyLogin = $method === "pubkey"
export let hasRelay = r => Boolean(find(propEq("url", r.url), $relays))
$: joined = find(propEq("url", relay.url), $relays)
export let removeRelay = r => user.removeRelay(r.url)
const removeRelay = ({url}) => user.removeRelay(url)
export let addRelay = r => {
user.addRelay(r.url).then(() => {
const pubkey = user.getPubkey()
const profile = user.getProfile()
const addRelay = async ({url}) => {
await user.addRelay(url)
if (!user.getProfile()?.kind0) {
loadAppData(user.getPubkey())
}
if (pubkey && !profile?.kind0) {
loadAppData(pubkey)
}
})
}
onMount(() => {
return poll(10_000, async () => {
;[quality, message] = await pool.getQuality(relay.url)
return poll(10_000, () => {
;[quality, message] = pool.getQuality(relay.url)
})
})
</script>
<RelayCard
{relay}
{theme}
addRelay={!joined && !isPubkeyLogin ? addRelay : null}
removeRelay={joined && $relays.length > 1 && !isPubkeyLogin ? removeRelay : null}>
<div
slot="controls"
class="flex justify-between gap-2"
class:hidden={!showControls || !$canPublish}>
<span>Publish to this relay?</span>
<Toggle
value={relay.write}
on:change={() => user.setRelayWriteCondition(relay.url, !relay.write)} />
<div
class={cx(
`bg-${theme}`,
"flex flex-col justify-between gap-3 rounded border border-l-2 border-solid border-gray-6 py-3 px-6 shadow"
)}
style={`border-left-color: ${hsl(stringToHue(relay.url))}`}
in:fly={{y: 20}}>
<div class="flex items-center justify-between gap-2">
<div class="flex items-center gap-2 text-xl">
<i class={relay.url.startsWith("wss") ? "fa fa-lock" : "fa fa-unlock"} />
<Anchor type="unstyled" href={`/relays/${btoa(relay.url)}`}>
{last(relay.url.split("://"))}
</Anchor>
{#if showStatus}
<span
on:mouseout={() => {
statusHover = false
}}
on:mouseover={() => {
statusHover = true
}}
class="h-2 w-2 cursor-pointer rounded-full bg-gray-6"
class:bg-gray-6={message === "Not connected"}
class:bg-danger={quality <= 0.3 && message !== "Not connected"}
class:bg-warning={between(0.3, 0.7, quality)}
class:bg-success={quality > 0.7} />
<p
class="hidden text-sm text-gray-1 transition-all sm:block"
class:opacity-0={!statusHover}
class:opacity-1={statusHover}>
{message}
</p>
{/if}
</div>
{#if $canPublish && showActions}
<slot name="actions">
{#if hasRelay(relay) && $relays.length > 1}
<button class="flex items-center gap-3 text-gray-1" on:click={() => removeRelay(relay)}>
<i class="fa fa-right-from-bracket" /> Leave
</button>
{/if}
{#if !hasRelay(relay)}
<button class="flex items-center gap-3 text-gray-1" on:click={() => addRelay(relay)}>
<i class="fa fa-right-to-bracket" /> Join
</button>
{/if}
</slot>
{/if}
</div>
</RelayCard>
{#if relay.description}
<p>{relay.description}</p>
{/if}
{#if hasRelay(relay) && showControls && $canPublish}
<div class="flex justify-between gap-2">
<span>Publish to this relay?</span>
<Toggle
value={relay.write}
on:change={() => user.setRelayWriteCondition(relay.url, !relay.write)} />
</div>
{/if}
</div>

View File

@ -6,12 +6,15 @@
import {watch} from "src/agent/storage"
import user from "src/agent/user"
let q = ""
export let q = ""
export let limit = 50
export let relays = user.relays
export let placeholder = "Search known relays"
export let hideIfEmpty = false
let search
let knownRelays = watch("relays", t => t.all())
const {relays} = user
$: {
const joined = new Set(pluck("url", $relays))
@ -22,13 +25,19 @@
}
</script>
<Input bind:value={q} type="text" wrapperClass="flex-grow" placeholder="Type to search">
<i slot="before" class="fa-solid fa-search" />
</Input>
{#each (search(q) || []).slice(0, 50) as relay (relay.url)}
<RelayCard {relay} />
{/each}
<small class="text-center">
Showing {Math.min(($knownRelays || []).length - $relays.length, 50)}
of {($knownRelays || []).length - $relays.length} known relays
</small>
<div class="flex flex-col gap-2">
<Input bind:value={q} type="text" wrapperClass="flex-grow" {placeholder}>
<i slot="before" class="fa-solid fa-search" />
</Input>
{#each !q && hideIfEmpty ? [] : search(q).slice(0, limit) as relay (relay.url)}
<slot name="item" {relay}>
<RelayCard {relay} />
</slot>
{/each}
<slot name="footer">
<small class="text-center">
Showing {Math.min(($knownRelays || []).length - $relays.length, 50)}
of {($knownRelays || []).length - $relays.length} known relays
</small>
</slot>
</div>

View File

@ -9,9 +9,9 @@
import Spinner from "src/partials/Spinner.svelte"
import Input from "src/partials/Input.svelte"
import Heading from "src/partials/Heading.svelte"
import RelayCardSimple from "src/partials/RelayCardSimple.svelte"
import Anchor from "src/partials/Anchor.svelte"
import Modal from "src/partials/Modal.svelte"
import RelayCard from "src/app2/shared/RelayCard.svelte"
import {watch} from "src/agent/storage"
import network from "src/agent/network"
import user from "src/agent/user"
@ -122,7 +122,7 @@
{#each Object.values(currentRelays) as relay}
<div class="h-12">
{#if relay}
<RelayCardSimple relay={{...relay, description: null}} />
<RelayCard relay={{...relay, description: null}} />
{/if}
</div>
{/each}

View File

@ -4,20 +4,20 @@
import {quantify} from "hurdak/lib/hurdak"
import {last, reject, pluck, propEq} from "ramda"
import {fly} from "svelte/transition"
import {fuzzy, annotateMedia} from "src/util/misc"
import {writable} from "svelte/store"
import {annotateMedia} from "src/util/misc"
import {displayPerson} from "src/util/nostr"
import Button from "src/partials/Button.svelte"
import Compose from "src/partials/Compose.svelte"
import ImageInput from "src/partials/ImageInput.svelte"
import Media from "src/partials/Media.svelte"
import Input from "src/partials/Input.svelte"
import RelayCardSimple from "src/partials/RelayCardSimple.svelte"
import Content from "src/partials/Content.svelte"
import Modal from "src/partials/Modal.svelte"
import Heading from "src/partials/Heading.svelte"
import RelayCard from "src/app2/shared/RelayCard.svelte"
import RelaySearch from "src/app2/shared/RelaySearch.svelte"
import {getUserWriteRelays} from "src/agent/relays"
import {getPersonWithFallback} from "src/agent/tables"
import {watch} from "src/agent/storage"
import cmd from "src/agent/cmd"
import user from "src/agent/user"
import {toast, modal} from "src/app/ui"
@ -26,23 +26,11 @@
export let pubkey = null
export let nevent = null
let q = ""
let image = null
let compose = null
let relays = getUserWriteRelays()
let showSettings = false
let q = ""
let search
const knownRelays = watch("relays", t => t.all())
$: {
const joined = new Set(pluck("url", relays))
search = fuzzy(
$knownRelays.filter(r => !joined.has(r.url)),
{keys: ["name", "description", "url"]}
)
}
let relays = writable(getUserWriteRelays())
const onSubmit = async () => {
let {content, mentions, topics} = compose.parse()
@ -53,7 +41,7 @@
if (content) {
const thunk = cmd.createNote(content.trim(), mentions, topics)
const [event, promise] = await publishWithToast(relays, thunk)
const [event, promise] = await publishWithToast($relays, thunk)
promise.then(() =>
setTimeout(
@ -66,7 +54,7 @@
"/" +
nip19.neventEncode({
id: event.id,
relays: pluck("url", relays.slice(0, 3)),
relays: pluck("url", $relays.slice(0, 3)),
}),
},
}),
@ -85,11 +73,11 @@
const addRelay = relay => {
q = ""
relays = relays.concat(relay)
relays.update($r => $r.concat(relay))
}
const removeRelay = relay => {
relays = reject(propEq("url", relay.url), relays)
relays.update(reject(propEq("url", relay.url)))
}
onMount(() => {
@ -134,7 +122,7 @@
on:click={() => {
showSettings = true
}}>
<span>Publishing to {quantify(relays.length, "relay")}</span>
<span>Publishing to {quantify($relays.length, "relay")}</span>
<i class="fa fa-edit" />
</small>
</div>
@ -150,7 +138,7 @@
</div>
<div>Select which relays to publish to:</div>
<div>
{#each relays as relay}
{#each $relays as relay}
<div
class="mr-1 mb-2 inline-block rounded-full border border-solid border-gray-1 py-1 px-2">
<button
@ -161,17 +149,21 @@
</div>
{/each}
</div>
<Input bind:value={q} placeholder="Search for other relays">
<i slot="before" class="fa fa-search" />
</Input>
{#each (q ? search(q) : []).slice(0, 3) as relay (relay.url)}
<RelayCardSimple {relay}>
<button slot="actions" class="underline" on:click={() => addRelay(relay)}>
Add relay
</button>
</RelayCardSimple>
{/each}
<Button type="submit" class="text-center">Done</Button>
<RelaySearch bind:q limit={3} hideIfEmpty>
<div slot="item" let:relay>
<RelayCard {relay} showActions>
<button
slot="actions"
class="underline"
on:click|preventDefault={() => addRelay(relay)}>
Add relay
</button>
</RelayCard>
</div>
<div slot="footer">
<Button type="submit" class="w-full text-center">Done</Button>
</div>
</RelaySearch>
</Content>
</form>
</Modal>

View File

@ -1,11 +1,11 @@
<script lang="ts">
import {reject, pluck, whereEq} from "ramda"
import {reject, always, pluck, propEq} from "ramda"
import {fuzzy} from "src/util/misc"
import Input from "src/partials/Input.svelte"
import Anchor from "src/partials/Anchor.svelte"
import Heading from "src/partials/Heading.svelte"
import Content from "src/partials/Content.svelte"
import RelayCard from "src/partials/RelayCard.svelte"
import RelayCard from "src/app2/shared/RelayCard.svelte"
import {watch} from "src/agent/storage"
import {modal} from "src/app/ui"
@ -16,22 +16,19 @@
const knownRelays = watch("relays", t => t.all())
$: {
const joined = new Set(pluck("url", relays))
search = fuzzy(
$knownRelays.filter(r => !joined.has(r.url)),
{keys: ["name", "description", "url"]}
)
const removeRelay = r => {
relays = reject(propEq("url", r.url), relays)
}
const removeRelay = ({url}) => {
relays = reject(whereEq({url}), relays)
const addRelay = r => {
relays = relays.concat({...r, write: true})
}
const addRelay = relay => {
relays = relays.concat({...relay, write: true})
}
$: joined = new Set(pluck("url", relays))
$: search = fuzzy(
$knownRelays.filter(r => !joined.has(r.url)),
{keys: ["name", "description", "url"]}
)
</script>
<Content>
@ -58,7 +55,7 @@
{:else}
<div class="grid grid-cols-1 gap-4">
{#each relays as relay (relay.url)}
<RelayCard {relay} {removeRelay} />
<RelayCard {relay} hasRelay={always(true)} {removeRelay} />
{/each}
</div>
{/if}
@ -70,7 +67,7 @@
<i slot="before" class="fa-solid fa-search" />
</Input>
{#each (search(q) || []).slice(0, 50) as relay (relay.url)}
<RelayCard {relay} {addRelay} />
<RelayCard {relay} hasRelay={always(false)} {addRelay} />
{/each}
<small class="text-center">
Showing {Math.min($knownRelays.length - relays.length, 50)}

View File

@ -1,74 +0,0 @@
<script lang="ts">
import cx from "classnames"
import {last} from "ramda"
import {onMount} from "svelte"
import {poll, stringToHue, hsl} from "src/util/misc"
import {between} from "hurdak/lib/hurdak"
import {fly} from "svelte/transition"
import Anchor from "src/partials/Anchor.svelte"
import pool from "src/agent/pool"
export let relay
export let theme = "gray-8"
export let removeRelay = null
export let addRelay = null
let quality = null
let message = null
let showStatus = false
onMount(() => {
return poll(10_000, async () => {
;[quality, message] = pool.getQuality(relay.url)
})
})
</script>
<div
class={cx(
`bg-${theme}`,
"flex flex-col justify-between gap-3 rounded border border-l-2 border-solid border-gray-6 py-3 px-6 shadow"
)}
style={`border-left-color: ${hsl(stringToHue(relay.url))}`}
in:fly={{y: 20}}>
<div class="flex items-center justify-between gap-2">
<div class="flex items-center gap-2 text-xl">
<i class={relay.url.startsWith("wss") ? "fa fa-lock" : "fa fa-unlock"} />
<Anchor type="unstyled" href={`/relays/${btoa(relay.url)}`}>
{last(relay.url.split("://"))}
</Anchor>
<span
on:mouseout={() => {
showStatus = false
}}
on:mouseover={() => {
showStatus = true
}}
class="h-2 w-2 cursor-pointer rounded-full bg-gray-6"
class:bg-gray-6={message === "Not connected"}
class:bg-danger={quality <= 0.3 && message !== "Not connected"}
class:bg-warning={between(0.3, 0.7, quality)}
class:bg-success={quality > 0.7} />
<p
class="hidden text-sm text-gray-1 transition-all sm:block"
class:opacity-0={!showStatus}
class:opacity-1={showStatus}>
{message}
</p>
</div>
{#if removeRelay}
<button class="flex items-center gap-3 text-gray-1" on:click={() => removeRelay(relay)}>
<i class="fa fa-right-from-bracket" /> Leave
</button>
{/if}
{#if addRelay}
<button class="flex items-center gap-3 text-gray-1" on:click={() => addRelay(relay)}>
<i class="fa fa-right-to-bracket" /> Join
</button>
{/if}
</div>
{#if relay.description}
<p>{relay.description}</p>
{/if}
<slot name="controls" />
</div>

View File

@ -1,24 +0,0 @@
<script lang="ts">
import {last} from "ramda"
import {fly} from "svelte/transition"
import {stringToHue, hsl} from "src/util/misc"
export let relay
</script>
<div
class="flex flex-col justify-between gap-3 rounded border border-l-2 border-solid
border-gray-6 py-3 px-6 shadow"
style={`border-left-color: ${hsl(stringToHue(relay.url))}`}
in:fly={{y: 20}}>
<div class="flex items-center justify-between gap-2">
<div class="flex items-center gap-2 text-xl">
<i class={relay.url.startsWith("wss") ? "fa fa-lock" : "fa fa-unlock"} />
<span>{last(relay.url.split("://"))}</span>
</div>
<slot name="actions" />
</div>
{#if relay.description}
<p>{relay.description}</p>
{/if}
</div>