From 8e411daaef064525bc580258f5b7cefa341d3ec5 Mon Sep 17 00:00:00 2001 From: Jon Staab Date: Tue, 11 Nov 2025 13:39:32 -0800 Subject: [PATCH] Refactor avatar components, add space edit form --- src/app/components/ChatMessage.svelte | 16 +- src/app/components/ContentMention.svelte | 4 +- src/app/components/MenuSpacesItem.svelte | 4 +- src/app/components/PrimaryNav.svelte | 41 ++-- src/app/components/PrimaryNavItemSpace.svelte | 4 +- src/app/components/Profile.svelte | 16 +- src/app/components/ProfileCircle.svelte | 21 +- src/app/components/ProfileCircles.svelte | 11 +- src/app/components/ProfileDetail.svelte | 4 +- src/app/components/ProfileInfo.svelte | 4 +- src/app/components/ProfileName.svelte | 4 +- src/app/components/ProfileSpaces.svelte | 4 +- src/app/components/RelayIcon.svelte | 21 ++ src/app/components/RelayName.svelte | 6 +- src/app/components/RoomItem.svelte | 9 +- src/app/components/SpaceAvatar.svelte | 20 -- src/app/components/SpaceDetail.svelte | 24 ++- src/app/components/SpaceEdit.svelte | 202 ++++++++++++++++++ src/app/components/ThreadItem.svelte | 2 +- src/app/core/commands.ts | 6 +- src/app/core/requests.ts | 4 +- src/app/core/state.ts | 8 +- src/app/editor/MentionNodeView.ts | 4 +- src/app/editor/ProfileSuggestion.svelte | 4 +- src/app/util/nip46.ts | 9 +- src/lib/components/Avatar.svelte | 30 --- src/lib/components/CardButton.svelte | 4 +- src/lib/components/ImageIcon.svelte | 7 +- src/lib/components/PrimaryNavItem.svelte | 4 +- src/routes/home/+page.svelte | 6 +- src/routes/settings/profile/+page.svelte | 4 +- src/routes/spaces/[relay]/[h]/+page.svelte | 6 +- 32 files changed, 356 insertions(+), 157 deletions(-) create mode 100644 src/app/components/RelayIcon.svelte delete mode 100644 src/app/components/SpaceAvatar.svelte create mode 100644 src/app/components/SpaceEdit.svelte delete mode 100644 src/lib/components/Avatar.svelte diff --git a/src/app/components/ChatMessage.svelte b/src/app/components/ChatMessage.svelte index 48998fa8..16f88dea 100644 --- a/src/app/components/ChatMessage.svelte +++ b/src/app/components/ChatMessage.svelte @@ -2,21 +2,14 @@ import {type Instance} from "tippy.js" import {hash, formatTimestampAsTime} from "@welshman/lib" import type {TrustedEvent, EventContent} from "@welshman/util" - import { - thunks, - mergeThunks, - pubkey, - deriveProfile, - deriveProfileDisplay, - sendWrapped, - } from "@welshman/app" + import {thunks, mergeThunks, pubkey, deriveProfileDisplay, sendWrapped} from "@welshman/app" import {isMobile} from "@lib/html" import MenuDots from "@assets/icons/menu-dots.svg?dataurl" import Icon from "@lib/components/Icon.svelte" import Button from "@lib/components/Button.svelte" import Tippy from "@lib/components/Tippy.svelte" import TapTarget from "@lib/components/TapTarget.svelte" - import Avatar from "@lib/components/Avatar.svelte" + import ProfileCircle from "@app/components/ProfileCircle.svelte" import Content from "@app/components/Content.svelte" import ReactionSummary from "@app/components/ReactionSummary.svelte" import ThunkFailure from "@app/components/ThunkFailure.svelte" @@ -37,7 +30,6 @@ const {event, replyTo, pubkeys, showPubkey = false}: Props = $props() const isOwn = event.pubkey === $pubkey - const profile = deriveProfile(event.pubkey) const profileDisplay = deriveProfileDisplay(event.pubkey) const thunk = mergeThunks($thunks.filter(t => t.event.id === event.id)) const [_, colorValue] = colors[hash(event.pubkey) % colors.length] @@ -107,8 +99,8 @@
{#if !isOwn}
- + Open in Coracle {:else}
diff --git a/src/app/components/SpaceAvatar.svelte b/src/app/components/SpaceAvatar.svelte deleted file mode 100644 index a6d1c3bf..00000000 --- a/src/app/components/SpaceAvatar.svelte +++ /dev/null @@ -1,20 +0,0 @@ - - - diff --git a/src/app/components/SpaceDetail.svelte b/src/app/components/SpaceDetail.svelte index 15f52d5e..b4e27edd 100644 --- a/src/app/components/SpaceDetail.svelte +++ b/src/app/components/SpaceDetail.svelte @@ -2,16 +2,22 @@ import {displayRelayUrl} from "@welshman/util" import {deriveRelay} from "@welshman/app" import UserRounded from "@assets/icons/user-rounded.svg?dataurl" + import AltArrowLeft from "@assets/icons/alt-arrow-left.svg?dataurl" + import Pen from "@assets/icons/pen.svg?dataurl" import ShieldUser from "@assets/icons/shield-user.svg?dataurl" import BillList from "@assets/icons/bill-list.svg?dataurl" import Ghost from "@assets/icons/ghost-smile.svg?dataurl" import Icon from "@lib/components/Icon.svelte" import Link from "@lib/components/Link.svelte" + import ModalFooter from "@lib/components/ModalFooter.svelte" import Button from "@lib/components/Button.svelte" import RelayName from "@app/components/RelayName.svelte" + import SpaceEdit from "@app/components/SpaceEdit.svelte" import SpaceRelayStatus from "@app/components/SpaceRelayStatus.svelte" import RelayDescription from "@app/components/RelayDescription.svelte" import ProfileLatest from "@app/components/ProfileLatest.svelte" + import {deriveUserIsSpaceAdmin} from "@app/core/state" + import {pushModal} from "@app/util/modal" type Props = { url: string @@ -20,8 +26,11 @@ const {url}: Props = $props() const relay = deriveRelay(url) const owner = $derived($relay?.pubkey) + const userIsAdmin = deriveUserIsSpaceAdmin(url) const back = () => history.back() + + const startEdit = () => pushModal(SpaceEdit, {url, initialValues: $relay})
@@ -78,5 +87,18 @@
{/if}
- + {#if $userIsAdmin} + + + + + {:else} + + {/if}
diff --git a/src/app/components/SpaceEdit.svelte b/src/app/components/SpaceEdit.svelte new file mode 100644 index 00000000..8b363dd3 --- /dev/null +++ b/src/app/components/SpaceEdit.svelte @@ -0,0 +1,202 @@ + + +
+ + {#snippet title()} +
Edit a Space
+ {/snippet} + {#snippet info()} + {displayRelayUrl(url)} + {/snippet} +
+ + {#snippet label()} +

Icon

+ {/snippet} + {#snippet input()} +
+
+ {#if imagePreview} +
+ Selected: + +
+ {:else} + No icon selected + {/if} +
+ + + Select + + +
+
+
+ {/snippet} +
+ + {#snippet label()} +

Name

+ {/snippet} + {#snippet input()} + + {/snippet} +
+ + {#snippet label()} +

Description

+ {/snippet} + {#snippet input()} + + {/snippet} +
+ + + + +
diff --git a/src/app/components/ThreadItem.svelte b/src/app/components/ThreadItem.svelte index 1fc6f93e..c194e2b1 100644 --- a/src/app/components/ThreadItem.svelte +++ b/src/app/components/ThreadItem.svelte @@ -21,7 +21,7 @@ {#if title}
diff --git a/src/app/core/commands.ts b/src/app/core/commands.ts index f33182c0..e2feb3cf 100644 --- a/src/app/core/commands.ts +++ b/src/app/core/commands.ts @@ -660,6 +660,8 @@ export const getBlossomServer = async (options: GetBlossomServerOptions = {}) => export type UploadFileOptions = { url?: string encrypt?: boolean + maxWidth?: number + maxHeight?: number } export type UploadFileResult = { @@ -671,8 +673,8 @@ export const uploadFile = async (file: File, options: UploadFileOptions = {}) => try { const {name, type} = file - if (!type.match("image/(webp|gif)")) { - file = await compressFile(file) + if (!type.match("image/(webp|gif|svg)")) { + file = await compressFile(file, options) } const tags: string[][] = [] diff --git a/src/app/core/requests.ts b/src/app/core/requests.ts index 7a3a53a8..4e6b6890 100644 --- a/src/app/core/requests.ts +++ b/src/app/core/requests.ts @@ -8,7 +8,7 @@ import { sortBy, now, on, - isNotNil, + isDefined, filterVals, fromPairs, } from "@welshman/lib" @@ -279,6 +279,6 @@ export const requestRelayClaim = async (url: string) => { export const requestRelayClaims = async (urls: string[]) => filterVals( - isNotNil, + isDefined, fromPairs(await Promise.all(urls.map(async url => [url, await requestRelayClaim(url)]))), ) diff --git a/src/app/core/state.ts b/src/app/core/state.ts index 97fe3d18..cf6db6de 100644 --- a/src/app/core/state.ts +++ b/src/app/core/state.ts @@ -730,7 +730,7 @@ export const deriveSpaceMembers = (url: string) => return getTagValues("member", membersEvent.tags) } - const members = new Set() + const members = new Set() for (const event of sortBy(e => e.created_at, $events)) { const pubkeys = getPubkeyTagValues(event.tags) @@ -765,7 +765,7 @@ export const deriveRoomMembers = (url: string, h: string) => return getPubkeyTagValues(membersEvent.tags) } - const members = new Set() + const members = new Set() for (const event of sortBy(e => -e.created_at, $events)) { const pubkeys = getPubkeyTagValues(event.tags) @@ -825,7 +825,7 @@ export const deriveUserSpaceMembershipStatus = (url: string) => deriveUserIsSpaceAdmin(url), ], ([$pubkey, $members, $events, $isAdmin]) => { - const isMember = $members.includes($pubkey) || $isAdmin + const isMember = $members.includes($pubkey!) || $isAdmin for (const event of $events) { if (event.pubkey !== $pubkey) { @@ -860,7 +860,7 @@ export const deriveUserRoomMembershipStatus = (url: string, h: string) => deriveUserIsRoomAdmin(url, h), ], ([$pubkey, $members, $events, $isAdmin]) => { - const isMember = $members.includes($pubkey) || $isAdmin + const isMember = $members.includes($pubkey!) || $isAdmin for (const event of $events) { if (event.pubkey !== $pubkey) { diff --git a/src/app/editor/MentionNodeView.ts b/src/app/editor/MentionNodeView.ts index 869fb7ea..7daf06e6 100644 --- a/src/app/editor/MentionNodeView.ts +++ b/src/app/editor/MentionNodeView.ts @@ -1,12 +1,12 @@ import type {NodeViewProps} from "@tiptap/core" -import {removeNil} from "@welshman/lib" +import {removeUndefined} from "@welshman/lib" import {deriveProfileDisplay} from "@welshman/app" export const makeMentionNodeView = (url?: string) => ({node}: NodeViewProps) => { const dom = document.createElement("span") - const display = deriveProfileDisplay(node.attrs.pubkey, removeNil([url])) + const display = deriveProfileDisplay(node.attrs.pubkey, removeUndefined([url])) dom.classList.add("tiptap-object") diff --git a/src/app/editor/ProfileSuggestion.svelte b/src/app/editor/ProfileSuggestion.svelte index 42620bcb..171e1393 100644 --- a/src/app/editor/ProfileSuggestion.svelte +++ b/src/app/editor/ProfileSuggestion.svelte @@ -1,5 +1,5 @@ diff --git a/src/app/util/nip46.ts b/src/app/util/nip46.ts index b7d9f396..845012c3 100644 --- a/src/app/util/nip46.ts +++ b/src/app/util/nip46.ts @@ -1,13 +1,7 @@ import {writable} from "svelte/store" import type {Nip46ResponseWithResult} from "@welshman/signer" import {Nip46Broker, makeSecret} from "@welshman/signer" -import { - NIP46_PERMS, - PLATFORM_URL, - PLATFORM_NAME, - PLATFORM_LOGO, - SIGNER_RELAYS, -} from "@app/core/state" +import {PLATFORM_URL, PLATFORM_NAME, PLATFORM_LOGO, SIGNER_RELAYS} from "@app/core/state" import {pushToast} from "@app/util/toast" export class Nip46Controller { @@ -25,7 +19,6 @@ export class Nip46Controller { async start() { const url = await this.broker.makeNostrconnectUrl({ - perms: NIP46_PERMS, url: PLATFORM_URL, name: PLATFORM_NAME, image: PLATFORM_LOGO, diff --git a/src/lib/components/Avatar.svelte b/src/lib/components/Avatar.svelte deleted file mode 100644 index 74e69a4b..00000000 --- a/src/lib/components/Avatar.svelte +++ /dev/null @@ -1,30 +0,0 @@ - - -
- -
diff --git a/src/lib/components/CardButton.svelte b/src/lib/components/CardButton.svelte index 0ba7556d..0695dde3 100644 --- a/src/lib/components/CardButton.svelte +++ b/src/lib/components/CardButton.svelte @@ -12,8 +12,8 @@
-
-
+
+
{@render props.icon?.()}
diff --git a/src/lib/components/ImageIcon.svelte b/src/lib/components/ImageIcon.svelte index c0ab10b6..44d51eea 100644 --- a/src/lib/components/ImageIcon.svelte +++ b/src/lib/components/ImageIcon.svelte @@ -5,13 +5,14 @@ src: string alt: string size?: number + class?: string } - const {src, alt, size = 5}: Props = $props() + const {src, alt, size = 5, ...props}: Props = $props() {#if src.includes("image/svg") || src.endsWith(".svg")} - + {:else} - + {/if} diff --git a/src/lib/components/PrimaryNavItem.svelte b/src/lib/components/PrimaryNavItem.svelte index d58329be..e13efd03 100644 --- a/src/lib/components/PrimaryNavItem.svelte +++ b/src/lib/components/PrimaryNavItem.svelte @@ -18,7 +18,7 @@ {#if href}
@@ -31,7 +31,7 @@ {:else}