Update pomade implementation
This commit is contained in:
parent
eeacaca725
commit
71f162f20d
12 changed files with 166 additions and 94 deletions
22
package.json
22
package.json
|
|
@ -57,7 +57,7 @@
|
||||||
"@getalby/lightning-tools": "^6.1.0",
|
"@getalby/lightning-tools": "^6.1.0",
|
||||||
"@getalby/sdk": "^5.1.2",
|
"@getalby/sdk": "^5.1.2",
|
||||||
"@noble/curves": "^1.9.7",
|
"@noble/curves": "^1.9.7",
|
||||||
"@pomade/core": "^0.1.1",
|
"@pomade/core": "^0.2.0",
|
||||||
"@poppanator/sveltekit-svg": "^4.2.1",
|
"@poppanator/sveltekit-svg": "^4.2.1",
|
||||||
"@sveltejs/adapter-static": "^3.0.10",
|
"@sveltejs/adapter-static": "^3.0.10",
|
||||||
"@tiptap/core": "^2.27.2",
|
"@tiptap/core": "^2.27.2",
|
||||||
|
|
@ -65,16 +65,16 @@
|
||||||
"@types/throttle-debounce": "^5.0.2",
|
"@types/throttle-debounce": "^5.0.2",
|
||||||
"@vite-pwa/assets-generator": "^0.2.6",
|
"@vite-pwa/assets-generator": "^0.2.6",
|
||||||
"@vite-pwa/sveltekit": "^0.6.8",
|
"@vite-pwa/sveltekit": "^0.6.8",
|
||||||
"@welshman/app": "^0.8.7",
|
"@welshman/app": "^0.8.8",
|
||||||
"@welshman/content": "^0.8.7",
|
"@welshman/content": "^0.8.8",
|
||||||
"@welshman/editor": "^0.8.7",
|
"@welshman/editor": "^0.8.8",
|
||||||
"@welshman/feeds": "^0.8.7",
|
"@welshman/feeds": "^0.8.8",
|
||||||
"@welshman/lib": "^0.8.7",
|
"@welshman/lib": "^0.8.8",
|
||||||
"@welshman/net": "^0.8.7",
|
"@welshman/net": "^0.8.8",
|
||||||
"@welshman/router": "^0.8.7",
|
"@welshman/router": "^0.8.8",
|
||||||
"@welshman/signer": "^0.8.7",
|
"@welshman/signer": "^0.8.8",
|
||||||
"@welshman/store": "^0.8.7",
|
"@welshman/store": "^0.8.8",
|
||||||
"@welshman/util": "^0.8.7",
|
"@welshman/util": "^0.8.8",
|
||||||
"compressorjs-next": "^1.1.2",
|
"compressorjs-next": "^1.1.2",
|
||||||
"daisyui": "^4.12.24",
|
"daisyui": "^4.12.24",
|
||||||
"date-picker-svelte": "^2.17.0",
|
"date-picker-svelte": "^2.17.0",
|
||||||
|
|
|
||||||
BIN
pnpm-lock.yaml
BIN
pnpm-lock.yaml
Binary file not shown.
|
|
@ -34,7 +34,6 @@
|
||||||
|
|
||||||
const onSuccess = async (session: Session) => {
|
const onSuccess = async (session: Session) => {
|
||||||
addSession(session)
|
addSession(session)
|
||||||
pushToast({message: "Successfully logged in!"})
|
|
||||||
setChecked("*")
|
setChecked("*")
|
||||||
clearModals()
|
clearModals()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@
|
||||||
import {pushModal, clearModals} from "@app/util/modal"
|
import {pushModal, clearModals} from "@app/util/modal"
|
||||||
import {setChecked} from "@app/util/notifications"
|
import {setChecked} from "@app/util/notifications"
|
||||||
import {pushToast} from "@app/util/toast"
|
import {pushToast} from "@app/util/toast"
|
||||||
|
import {deleteOldPomadeSessions} from "@app/core/commands"
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
email?: string
|
email?: string
|
||||||
|
|
@ -51,7 +52,7 @@
|
||||||
|
|
||||||
if (res.ok && clientOptions) {
|
if (res.ok && clientOptions) {
|
||||||
loginWithPomade(clientOptions.group.group_pk.slice(2), email, clientOptions)
|
loginWithPomade(clientOptions.group.group_pk.slice(2), email, clientOptions)
|
||||||
pushToast({message: "Successfully logged in!"})
|
deleteOldPomadeSessions()
|
||||||
setChecked("*")
|
setChecked("*")
|
||||||
clearModals()
|
clearModals()
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -57,7 +57,6 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
loginWithNip01(secret)
|
loginWithNip01(secret)
|
||||||
pushToast({message: "Successfully logged in!"})
|
|
||||||
setChecked("*")
|
setChecked("*")
|
||||||
clearModals()
|
clearModals()
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
import {setChecked} from "@app/util/notifications"
|
import {setChecked} from "@app/util/notifications"
|
||||||
import {pushToast} from "@app/util/toast"
|
import {pushToast} from "@app/util/toast"
|
||||||
import {POMADE_SIGNERS} from "@app/core/state"
|
import {POMADE_SIGNERS} from "@app/core/state"
|
||||||
|
import {deleteOldPomadeSessions} from "@app/core/commands"
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
email: string
|
email: string
|
||||||
|
|
@ -63,7 +64,7 @@
|
||||||
|
|
||||||
if (res.ok && clientOptions) {
|
if (res.ok && clientOptions) {
|
||||||
loginWithPomade(clientOptions.group.group_pk.slice(2), email, clientOptions)
|
loginWithPomade(clientOptions.group.group_pk.slice(2), email, clientOptions)
|
||||||
pushToast({message: "Successfully logged in!"})
|
deleteOldPomadeSessions()
|
||||||
setChecked("*")
|
setChecked("*")
|
||||||
clearModals()
|
clearModals()
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -9,8 +9,7 @@
|
||||||
import ModalHeader from "@lib/components/ModalHeader.svelte"
|
import ModalHeader from "@lib/components/ModalHeader.svelte"
|
||||||
import ModalTitle from "@lib/components/ModalTitle.svelte"
|
import ModalTitle from "@lib/components/ModalTitle.svelte"
|
||||||
import ModalFooter from "@lib/components/ModalFooter.svelte"
|
import ModalFooter from "@lib/components/ModalFooter.svelte"
|
||||||
import {Push} from "@app/util/notifications"
|
import {logout} from "@app/util/logout"
|
||||||
import {kv, db} from "@app/core/storage"
|
|
||||||
|
|
||||||
const back = () => history.back()
|
const back = () => history.back()
|
||||||
|
|
||||||
|
|
@ -18,13 +17,7 @@
|
||||||
loading = true
|
loading = true
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await Push.disable()
|
await logout()
|
||||||
await kv.clear()
|
|
||||||
await db.clear()
|
|
||||||
|
|
||||||
localStorage.clear()
|
|
||||||
|
|
||||||
window.location.href = "/"
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e)
|
console.error(e)
|
||||||
loading = false
|
loading = false
|
||||||
|
|
|
||||||
|
|
@ -1,52 +1,31 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import {onMount} from "svelte"
|
||||||
import {Client} from "@pomade/core"
|
import {Client} from "@pomade/core"
|
||||||
import type {SessionItem} from "@pomade/core"
|
|
||||||
import {session, isPomadeSession} from "@welshman/app"
|
import {session, isPomadeSession} from "@welshman/app"
|
||||||
|
import MenuDots from "@assets/icons/menu-dots.svg?dataurl"
|
||||||
|
import {fly} from "@lib/transition"
|
||||||
import Icon from "@lib/components/Icon.svelte"
|
import Icon from "@lib/components/Icon.svelte"
|
||||||
import Button from "@lib/components/Button.svelte"
|
import Button from "@lib/components/Button.svelte"
|
||||||
|
import Popover from "@lib/components/Popover.svelte"
|
||||||
import TrashBin2 from "@assets/icons/trash-bin-2.svg?dataurl"
|
import TrashBin2 from "@assets/icons/trash-bin-2.svg?dataurl"
|
||||||
import {pushToast} from "@app/util/toast"
|
import {pushToast} from "@app/util/toast"
|
||||||
import {onMount} from "svelte"
|
import {loadOtherPomadeSessions} from "@app/core/commands"
|
||||||
|
import type {PomadeSessionWithPeers} from "@app/core/commands"
|
||||||
|
|
||||||
type SessionWithPeers = SessionItem & {peers: string[]}
|
const toggleMenu = (client: string) => {
|
||||||
|
menuClient = menuClient === client ? "" : client
|
||||||
|
}
|
||||||
|
|
||||||
let sessions = $state<SessionWithPeers[]>([])
|
const closeMenu = () => {
|
||||||
let deletingSession = $state<string | null>(null)
|
menuClient = ""
|
||||||
|
}
|
||||||
|
|
||||||
const loadSessions = async () => {
|
let menuClient = $state("")
|
||||||
|
let sessions = $state<PomadeSessionWithPeers[]>([])
|
||||||
|
|
||||||
|
const deleteSession = async (sessionItem: PomadeSessionWithPeers) => {
|
||||||
if (!isPomadeSession($session)) return
|
if (!isPomadeSession($session)) return
|
||||||
|
|
||||||
const client = new Client($session.clientOptions)
|
|
||||||
const result = await client.listSessions()
|
|
||||||
const pubkey = await client.getPubkey()
|
|
||||||
|
|
||||||
if (result.ok) {
|
|
||||||
// Group sessions by client pubkey and collect peers
|
|
||||||
const sessionMap = new Map<string, SessionWithPeers>()
|
|
||||||
|
|
||||||
for (const message of result.messages) {
|
|
||||||
if (!message.res?.items) continue
|
|
||||||
|
|
||||||
for (const item of message.res.items) {
|
|
||||||
const existing = sessionMap.get(item.client)
|
|
||||||
|
|
||||||
if (existing) {
|
|
||||||
existing.peers.push(message.url)
|
|
||||||
} else if (item.client !== pubkey) {
|
|
||||||
sessionMap.set(item.client, {...item, peers: [message.url]})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sessions = Array.from(sessionMap.values())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const deleteSession = async (sessionItem: SessionWithPeers) => {
|
|
||||||
if (!isPomadeSession($session)) return
|
|
||||||
|
|
||||||
deletingSession = sessionItem.client
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const client = new Client($session.clientOptions)
|
const client = new Client($session.clientOptions)
|
||||||
const result = await client.deleteSession(sessionItem.client, sessionItem.peers)
|
const result = await client.deleteSession(sessionItem.client, sessionItem.peers)
|
||||||
|
|
@ -70,8 +49,6 @@
|
||||||
theme: "error",
|
theme: "error",
|
||||||
message: "Failed to delete session",
|
message: "Failed to delete session",
|
||||||
})
|
})
|
||||||
} finally {
|
|
||||||
deletingSession = null
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -85,7 +62,9 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
loadSessions()
|
loadOtherPomadeSessions().then(_sessions => {
|
||||||
|
sessions = _sessions
|
||||||
|
})
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
@ -93,29 +72,47 @@
|
||||||
<div class="flex flex-col gap-4 border-t border-solid border-base-100 pt-4">
|
<div class="flex flex-col gap-4 border-t border-solid border-base-100 pt-4">
|
||||||
<strong>Other Sessions</strong>
|
<strong>Other Sessions</strong>
|
||||||
{#each sessions as sessionItem (sessionItem.client)}
|
{#each sessions as sessionItem (sessionItem.client)}
|
||||||
<div class="flex justify-between text-sm">
|
<div class="flex flex-col gap-2">
|
||||||
<div class="flex flex-col gap-1">
|
<div class="flex justify-between items-center">
|
||||||
<span>{sessionItem.client.slice(0, 8)}</span>
|
<div class="flex gap-3 items-center text-sm">
|
||||||
|
<span>Session {sessionItem.client.slice(0, 8)}</span>
|
||||||
|
<span class="opacity-75">
|
||||||
|
{#if sessionItem.deactivated_at}
|
||||||
|
Deactivated
|
||||||
|
{/if}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="relative">
|
||||||
|
<Button
|
||||||
|
class="btn btn-circle btn-ghost btn-sm"
|
||||||
|
onclick={() => toggleMenu(sessionItem.client)}>
|
||||||
|
<Icon icon={MenuDots} />
|
||||||
|
</Button>
|
||||||
|
{#if menuClient === sessionItem.client}
|
||||||
|
<Popover hideOnClick onClose={closeMenu}>
|
||||||
|
<ul
|
||||||
|
transition:fly
|
||||||
|
class="menu absolute right-0 z-popover mt-2 w-48 gap-1 rounded-box bg-base-100 p-2 shadow-md">
|
||||||
|
<li>
|
||||||
|
<Button onclick={() => deleteSession(sessionItem)}>
|
||||||
|
<Icon icon={TrashBin2} />
|
||||||
|
Delete Session
|
||||||
|
</Button>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</Popover>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="flex gap-1">
|
<div class="flex gap-1">
|
||||||
<div class="badge badge-neutral">
|
<div class="badge badge-neutral">
|
||||||
Created {formatDate(sessionItem.created_at)}
|
Created {formatDate(sessionItem.created_at)}
|
||||||
</div>
|
</div>
|
||||||
<div class="badge badge-neutral">
|
<div class="badge badge-neutral">
|
||||||
Last active: {formatDate(sessionItem.last_activity)}
|
Active {formatDate(sessionItem.last_activity)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Button
|
|
||||||
class="btn btn-error btn-sm"
|
|
||||||
disabled={deletingSession !== null}
|
|
||||||
onclick={() => deleteSession(sessionItem)}>
|
|
||||||
{#if deletingSession === sessionItem.client}
|
|
||||||
<span class="loading loading-spinner"></span>
|
|
||||||
{:else}
|
|
||||||
<Icon icon={TrashBin2} />
|
|
||||||
{/if}
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
|
||||||
|
|
@ -23,9 +23,8 @@
|
||||||
import Modal from "@lib/components/Modal.svelte"
|
import Modal from "@lib/components/Modal.svelte"
|
||||||
import ModalBody from "@lib/components/ModalBody.svelte"
|
import ModalBody from "@lib/components/ModalBody.svelte"
|
||||||
import {INDEXER_RELAYS, PLATFORM_NAME, userSpaceUrls} from "@app/core/state"
|
import {INDEXER_RELAYS, PLATFORM_NAME, userSpaceUrls} from "@app/core/state"
|
||||||
import {kv, db} from "@app/core/storage"
|
|
||||||
import {pushToast} from "@app/util/toast"
|
import {pushToast} from "@app/util/toast"
|
||||||
import {Push} from "@app/util/notifications"
|
import {logout} from "@app/util/logout"
|
||||||
|
|
||||||
let progress: number | undefined = $state(undefined)
|
let progress: number | undefined = $state(undefined)
|
||||||
let confirmText = $state("")
|
let confirmText = $state("")
|
||||||
|
|
@ -88,13 +87,7 @@
|
||||||
await sleep(2000)
|
await sleep(2000)
|
||||||
|
|
||||||
// Goodbye forever!
|
// Goodbye forever!
|
||||||
await Push.disable()
|
await logout()
|
||||||
await kv.clear()
|
|
||||||
await db.clear()
|
|
||||||
|
|
||||||
localStorage.clear()
|
|
||||||
|
|
||||||
window.location.href = "/"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const confirm = async () => {
|
const confirm = async () => {
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@
|
||||||
import ModalSubtitle from "@lib/components/ModalSubtitle.svelte"
|
import ModalSubtitle from "@lib/components/ModalSubtitle.svelte"
|
||||||
import ModalFooter from "@lib/components/ModalFooter.svelte"
|
import ModalFooter from "@lib/components/ModalFooter.svelte"
|
||||||
import SignUpEmailConfirm from "@app/components/SignUpEmailConfirm.svelte"
|
import SignUpEmailConfirm from "@app/components/SignUpEmailConfirm.svelte"
|
||||||
import {pushToast} from "@app/util/toast"
|
import {pushToast, popToast} from "@app/util/toast"
|
||||||
import {pushModal} from "@app/util/modal"
|
import {pushModal} from "@app/util/modal"
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
|
|
@ -40,6 +40,11 @@
|
||||||
loading = true
|
loading = true
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
const toastId = pushToast({
|
||||||
|
timeout: 60_000,
|
||||||
|
message: "Creating your account, please wait...",
|
||||||
|
})
|
||||||
|
|
||||||
const secret = getKey<string>("signup.secret")!
|
const secret = getKey<string>("signup.secret")!
|
||||||
const {clientOptions, ...registerRes} = await Client.register(2, 3, secret)
|
const {clientOptions, ...registerRes} = await Client.register(2, 3, secret)
|
||||||
|
|
||||||
|
|
@ -74,6 +79,7 @@
|
||||||
setKey("signup.email", email)
|
setKey("signup.email", email)
|
||||||
setKey("signup.clientOptions", clientOptions)
|
setKey("signup.clientOptions", clientOptions)
|
||||||
|
|
||||||
|
popToast(toastId)
|
||||||
pushModal(SignUpEmailConfirm, {next})
|
pushModal(SignUpEmailConfirm, {next})
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e)
|
console.error(e)
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
import {nwc} from "@getalby/sdk"
|
import {nwc} from "@getalby/sdk"
|
||||||
import * as nip19 from "nostr-tools/nip19"
|
import * as nip19 from "nostr-tools/nip19"
|
||||||
import {get, derived} from "svelte/store"
|
import {get, derived} from "svelte/store"
|
||||||
|
import {Client} from "@pomade/core"
|
||||||
|
import type {SessionItem} from "@pomade/core"
|
||||||
import {
|
import {
|
||||||
first,
|
first,
|
||||||
sha256,
|
sha256,
|
||||||
|
|
@ -71,6 +73,7 @@ import {
|
||||||
waitForThunkError,
|
waitForThunkError,
|
||||||
getPubkeyRelays,
|
getPubkeyRelays,
|
||||||
userBlossomServerList,
|
userBlossomServerList,
|
||||||
|
isPomadeSession,
|
||||||
getThunkError,
|
getThunkError,
|
||||||
} from "@welshman/app"
|
} from "@welshman/app"
|
||||||
import {compressFile} from "@lib/html"
|
import {compressFile} from "@lib/html"
|
||||||
|
|
@ -699,3 +702,58 @@ export const updateProfile = async ({
|
||||||
|
|
||||||
await publishThunk({event, relays}).complete
|
await publishThunk({event, relays}).complete
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Pomade
|
||||||
|
|
||||||
|
export type PomadeSessionWithPeers = SessionItem & {peers: string[]}
|
||||||
|
|
||||||
|
export const loadOtherPomadeSessions = async () => {
|
||||||
|
const $session = get(session)
|
||||||
|
|
||||||
|
if (isPomadeSession($session)) {
|
||||||
|
const client = new Client($session.clientOptions)
|
||||||
|
const result = await client.listSessions()
|
||||||
|
const pubkey = await client.getPubkey()
|
||||||
|
|
||||||
|
if (result.ok) {
|
||||||
|
// Group sessions by client pubkey and collect peers
|
||||||
|
const sessionMap = new Map<string, PomadeSessionWithPeers>()
|
||||||
|
|
||||||
|
for (const message of result.messages) {
|
||||||
|
if (!message.res?.items) continue
|
||||||
|
|
||||||
|
for (const item of message.res.items) {
|
||||||
|
if (item.client === pubkey) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
const existing = sessionMap.get(item.client)
|
||||||
|
|
||||||
|
if (existing) {
|
||||||
|
existing.peers.push(message.url)
|
||||||
|
} else {
|
||||||
|
sessionMap.set(item.client, {...item, peers: [message.url]})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Array.from(sessionMap.values())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
export const deleteOldPomadeSessions = async () => {
|
||||||
|
const $session = get(session)
|
||||||
|
|
||||||
|
if (isPomadeSession($session)) {
|
||||||
|
const client = new Client($session.clientOptions)
|
||||||
|
|
||||||
|
for (const item of await loadOtherPomadeSessions()) {
|
||||||
|
if (item.deactivated_at) {
|
||||||
|
await client.deleteSession(item.client, item.peers)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
25
src/app/util/logout.ts
Normal file
25
src/app/util/logout.ts
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
import {get} from "svelte/store"
|
||||||
|
import {Client} from "@pomade/core"
|
||||||
|
import {getPubkey} from "@welshman/util"
|
||||||
|
import {session, isPomadeSession} from "@welshman/app"
|
||||||
|
import {kv, db} from "@app/core/storage"
|
||||||
|
import {Push} from "@app/util/notifications"
|
||||||
|
|
||||||
|
export const logout = async () => {
|
||||||
|
const $session = get(session)
|
||||||
|
|
||||||
|
if ($session && isPomadeSession($session)) {
|
||||||
|
await new Client($session.clientOptions).deactivateSession(
|
||||||
|
getPubkey($session.clientOptions.secret),
|
||||||
|
$session.clientOptions.peers,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
await Push.disable()
|
||||||
|
await kv.clear()
|
||||||
|
await db.clear()
|
||||||
|
|
||||||
|
localStorage.clear()
|
||||||
|
|
||||||
|
window.location.href = "/"
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue