Add some state management stuff

This commit is contained in:
Jon Staab
2024-08-06 15:46:37 -07:00
parent 36a920df51
commit fb04a68168
18 changed files with 5474 additions and 5206 deletions

1
.gitattributes vendored Normal file
View File

@@ -0,0 +1 @@
package-lock.json -diff

10477
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -36,6 +36,9 @@
"dependencies": { "dependencies": {
"@poppanator/sveltekit-svg": "^4.2.1", "@poppanator/sveltekit-svg": "^4.2.1",
"@welshman/lib": "^0.0.12", "@welshman/lib": "^0.0.12",
"@welshman/net": "^0.0.16",
"@welshman/store": "^0.0.1",
"@welshman/util": "^0.0.23",
"daisyui": "^4.12.10", "daisyui": "^4.12.10",
"nostr-login": "^1.5.2", "nostr-login": "^1.5.2",
"prettier-plugin-tailwindcss": "^0.6.5" "prettier-plugin-tailwindcss": "^0.6.5"

View File

@@ -69,18 +69,22 @@
--stark-content: #111; --stark-content: #111;
} }
.text-stark { .text-stark,
.hover\:text-stark:hover {
color: var(--stark); color: var(--stark);
} }
.text-stark-content { .text-stark-content,
.hover\:text-stark-content:hover {
color: var(--stark-content); color: var(--stark-content);
} }
.bg-stark { .bg-stark,
.hover\:bg-stark:hover {
background-color: var(--stark); background-color: var(--stark);
} }
.bg-stark-content { .bg-stark-content,
.hover\:bg-stark-content:hover {
background-color: var(--stark-content); background-color: var(--stark-content);
} }

38
src/app/base.ts Normal file
View File

@@ -0,0 +1,38 @@
import type {SignedEvent} from "@welshman/util"
import {Repository, Relay} from "@welshman/util"
import {NetworkContext, Tracker} from "@welshman/net"
export const DUFFLEPUD_URL = "https://dufflepud.onrender.com"
export const repository = new Repository()
export const relay = new Relay(repository)
export const tracker = new Tracker()
const seenChallenges = new Set()
Object.assign(NetworkContext, {
onEvent: (url: string, event: SignedEvent) => tracker.track(event.id, url),
isDeleted: (url: string, event: SignedEvent) => repository.isDeleted(event),
// onAuth: async (url, challenge) => {
// if (seenChallenges.has(challenge)) {
// return
// }
// seenChallenges.add(challenge)
// const event = await signer.get().signAsUser(
// createEvent(22242, {
// tags: [
// ["relay", url],
// ["challenge", challenge],
// ],
// }),
// )
// NetworkContext.pool.get(url).send(["AUTH", event])
// return event
// },
})

15
src/app/commands.ts Normal file
View File

@@ -0,0 +1,15 @@
import {batch, postJson} from "@welshman/lib"
import {normalizeRelayUrl} from "@welshman/util"
import {relayInfo} from "app/state"
export const loadRelay = batch(1000, async (urls: string[]) => {
const data = await postJson(`${DUFFLEPUD_URL}/relay/info`, {urls})
relayInfo.update($relayInfo => {
for (const {url, info} of data) {
$relayInfo.set(normalizeRelayUrl(url), info)
}
return $relayInfo
})
})

View File

@@ -8,7 +8,8 @@
<script lang="ts"> <script lang="ts">
import Icon from "@lib/components/Icon.svelte" import Icon from "@lib/components/Icon.svelte"
import PrimaryNavItem from "@lib/components/PrimaryNavItem.svelte" import PrimaryNavItem from "@lib/components/PrimaryNavItem.svelte"
import {spaces} from "@app/state" import {getGroupName, getGroupPicture, makeGroupId} from "@app/domain"
import {userGroupRelaysByNom, groupsById} from "@app/state"
</script> </script>
<div class="relative w-14 bg-base-100"> <div class="relative w-14 bg-base-100">
@@ -22,10 +23,12 @@
src="https://img.daisyui.com/images/stock/photo-1534528741775-53994a69daeb.webp" /> src="https://img.daisyui.com/images/stock/photo-1534528741775-53994a69daeb.webp" />
</div> </div>
</PrimaryNavItem> </PrimaryNavItem>
{#each $spaces as { id, name, picture } (id)} {#each $userGroupRelaysByNom.entries() as [nom, relays] (nom)}
{@const event = $groupsById.get(makeGroupId(relays[0], nom))}
{@const name = getGroupName(event)}
<PrimaryNavItem title={name}> <PrimaryNavItem title={name}>
<div class="w-10 rounded-full border border-solid border-base-300"> <div class="w-10 rounded-full border border-solid border-base-300">
<img alt={name} src={picture} /> <img alt={name} src={getGroupPicture(event)} />
</div> </div>
</PrimaryNavItem> </PrimaryNavItem>
{/each} {/each}

View File

@@ -4,7 +4,7 @@
</script> </script>
<div class="flex w-60 flex-col gap-1 bg-base-300 px-2 py-4"> <div class="flex w-60 flex-col gap-1 bg-base-300 px-2 py-4">
<SecondaryNavItem href="/"> <SecondaryNavItem href="/home">
<Icon icon="home-smile" /> Home <Icon icon="home-smile" /> Home
</SecondaryNavItem> </SecondaryNavItem>
<SecondaryNavItem href="/people"> <SecondaryNavItem href="/people">

22
src/app/domain.ts Normal file
View File

@@ -0,0 +1,22 @@
import {stripProtocol} from "@welshman/lib"
import type {TrustedEvent} from "@welshman/util"
import {getIdentifier, normalizeRelayUrl} from "@welshman/util"
export const GROUP_DELIMITER = `'`
export const makeGroupId = (url: string, nom: string) =>
[stripProtocol(url), nom].join(GROUP_DELIMITER)
export const getGroupNom = (e: TrustedEvent) => getIdentifier(e)?.split(GROUP_DELIMITER)[1]
export const getGroupUrl = (e: TrustedEvent) => {
const id = getIdentifier(e)
const url = id?.split(GROUP_DELIMITER)[0]
return url ? normalizeRelayUrl(url) : null
}
export const getGroupName = (e: TrustedEvent | undefined) => e?.tags.find(t => t[0] === "name")?.[1]
export const getGroupPicture = (e: TrustedEvent | undefined) =>
e?.tags.find(t => t[0] === "picture")?.[1]

View File

@@ -1,5 +1,4 @@
import type {ComponentType} from "svelte" import type {ComponentType} from "svelte"
import {readable, writable} from "svelte/store"
import {randomId} from "@welshman/lib" import {randomId} from "@welshman/lib"
import {pushState} from "$app/navigation" import {pushState} from "$app/navigation"

View File

@@ -1,10 +1,44 @@
import {readable} from "svelte/store" import {writable, derived} from "svelte/store"
import {pushToMapKey, indexBy} from "@welshman/lib"
import {getIdentifier, GROUP_META, GROUPS, getGroupTagValues} from "@welshman/util"
import {deriveEvents} from "@welshman/store"
import {repository} from "@app/base"
import {getGroupUrl, GROUP_DELIMITER} from "@app/domain"
export const spaces = readable([ export const pk = writable<string | null>(null)
{
id: "test", export const sessions = writable(new Map())
name: "Test",
picture: export const session = derived([pk, sessions], ([$pk, $sessions]) => $sessions.get($pk))
"https://images.unsplash.com/photo-1721853046219-209921be684e?w=800&auto=format&fit=crop&q=60&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxmZWF0dXJlZC1waG90b3MtZmVlZHw0fHx8ZW58MHx8fHx8",
}, export const relayInfo = writable(new Map())
])
export const groupEvents = deriveEvents(repository, {
filters: [{kinds: [GROUP_META]}],
})
export const groups = derived([relayInfo, groupEvents], ([$relayInfo, $groupEvents]) =>
$groupEvents.filter(e => $relayInfo.get(getGroupUrl(e))?.pubkey === e.pubkey),
)
export const groupsById = derived(groups, $groups => indexBy(getIdentifier, $groups))
export const groupsEvents = deriveEvents(repository, {
filters: [{kinds: [GROUPS]}],
})
export const userGroupsEvent = derived([pk, groupsEvents], ([$pk, $groupsEvents]) =>
$groupsEvents.find(e => e.pubkey === $pk),
)
export const userGroupRelaysByNom = derived(userGroupsEvent, $userGroupsEvent => {
const relaysByNom = new Map()
for (const id of getGroupTagValues($userGroupsEvent?.tags || [])) {
const [relay, nom] = id.split(GROUP_DELIMITER)
pushToMapKey(relaysByNom, nom, relay)
}
return relaysByNom
})

View File

@@ -1,12 +1,17 @@
<script lang="ts"> <script lang="ts">
import {page} from "$app/stores"
export let href export let href
export let active
$: active = $page.route.id?.startsWith(href)
</script> </script>
<a {href} class="button group justify-start border-none transition-all hover:bg-base-100"> <a
<div {href}
class="group-hover:brightness=[1.5] flex items-center gap-3" class="button group justify-start border-none transition-all hover:bg-base-100"
class:group-hover:brightness-[1.4]={active}> class:text-stark-content={active}
class:bg-base-100={active}>
<div class="flex items-center gap-3">
<slot /> <slot />
</div> </div>
</a> </a>

View File

@@ -1 +0,0 @@

View File

@@ -0,0 +1,8 @@
import {redirect} from "@sveltejs/kit"
/** @type {import('./$types').LayoutServerLoad} */
export function load({route}) {
if (!route.id) {
redirect(307, "/home")
}
}

View File

@@ -1,12 +1,9 @@
<script lang="ts"> <script lang="ts">
import "@src/app.css" import "@src/app.css"
import {onMount} from "svelte"
import {page} from "$app/stores" import {page} from "$app/stores"
import {onNavigate} from "$app/navigation" import {fly} from "@lib/transition"
import {fly} from '@lib/transition' import {toast} from "@app/toast"
import Icon from "@lib/components/Icon.svelte" import {modals} from "@app/modal"
import {toast} from '@app/toast'
import {modals, pushModal} from '@app/modal'
import PrimaryNav from "@app/components/PrimaryNav.svelte" import PrimaryNav from "@app/components/PrimaryNav.svelte"
import SecondaryNav from "@app/components/SecondaryNav.svelte" import SecondaryNav from "@app/components/SecondaryNav.svelte"

View File

@@ -1,4 +1,3 @@
import * as path from "path"
import adapter from "@sveltejs/adapter-auto" import adapter from "@sveltejs/adapter-auto"
import {vitePreprocess} from "@sveltejs/vite-plugin-svelte" import {vitePreprocess} from "@sveltejs/vite-plugin-svelte"
@@ -14,10 +13,10 @@ const config = {
// See https://kit.svelte.dev/docs/adapters for more information about adapters. // See https://kit.svelte.dev/docs/adapters for more information about adapters.
adapter: adapter(), adapter: adapter(),
alias: { alias: {
'@src': "src", "@src": "src",
'@app': "src/app", "@app": "src/app",
'@lib': "src/lib", "@lib": "src/lib",
'@assets': "src/assets", "@assets": "src/assets",
}, },
}, },
} }

View File

@@ -1,3 +1,5 @@
import daisyui from "daisyui"
/** @type {import('tailwindcss').Config} */ /** @type {import('tailwindcss').Config} */
export default { export default {
content: ["./src/**/*.{html,js,svelte,ts}"], content: ["./src/**/*.{html,js,svelte,ts}"],
@@ -9,9 +11,7 @@ export default {
"nav-item": 2, "nav-item": 2,
}, },
}, },
plugins: [ plugins: [daisyui],
require("daisyui"),
],
daisyui: { daisyui: {
themes: [ themes: [
"light", "light",