From 119c09d73056bea5c75f6944b1ea6554735b58e6 Mon Sep 17 00:00:00 2001
From: Jon Staab
Date: Tue, 3 Feb 2026 11:00:13 -0800
Subject: [PATCH] Add classified listings
---
src/app/components/ClassifiedActions.svelte | 45 +++++
src/app/components/ClassifiedCreate.svelte | 149 ++++++++++++++++
src/app/components/ClassifiedItem.svelte | 49 ++++++
src/app/components/CommentActions.svelte | 11 +-
src/app/components/ComposeMenu.svelte | 14 +-
src/app/components/EventInfo.svelte | 5 +-
src/app/components/NoteContent.svelte | 5 +-
.../components/NoteContentClassified.svelte | 18 ++
src/app/components/NoteContentMinimal.svelte | 5 +-
.../NoteContentMinimalClassified.svelte | 16 ++
src/app/components/RoomCompose.svelte | 6 +-
src/app/components/SpaceMenu.svelte | 12 +-
src/app/core/state.ts | 16 +-
src/app/util/notifications.ts | 26 +++
src/app/util/routes.ts | 14 ++
src/app/util/storage.ts | 9 +-
src/lib/components/CurrencyInput.svelte | 23 +++
src/lib/components/CurrencySymbol.svelte | 11 ++
src/lib/components/Dialog.svelte | 2 +-
src/lib/currency.ts | 163 ++++++++++++++++++
.../spaces/[relay]/classifieds/+page.svelte | 104 +++++++++++
.../[relay]/classifieds/[id]/+page.svelte | 124 +++++++++++++
.../spaces/[relay]/goals/[id]/+page.svelte | 2 +-
src/routes/spaces/[relay]/recent/+page.svelte | 13 +-
.../spaces/[relay]/threads/[id]/+page.svelte | 2 +-
25 files changed, 816 insertions(+), 28 deletions(-)
create mode 100644 src/app/components/ClassifiedActions.svelte
create mode 100644 src/app/components/ClassifiedCreate.svelte
create mode 100644 src/app/components/ClassifiedItem.svelte
create mode 100644 src/app/components/NoteContentClassified.svelte
create mode 100644 src/app/components/NoteContentMinimalClassified.svelte
create mode 100644 src/lib/components/CurrencyInput.svelte
create mode 100644 src/lib/components/CurrencySymbol.svelte
create mode 100644 src/lib/currency.ts
create mode 100644 src/routes/spaces/[relay]/classifieds/+page.svelte
create mode 100644 src/routes/spaces/[relay]/classifieds/[id]/+page.svelte
diff --git a/src/app/components/ClassifiedActions.svelte b/src/app/components/ClassifiedActions.svelte
new file mode 100644
index 000000000..b8605a889
--- /dev/null
+++ b/src/app/components/ClassifiedActions.svelte
@@ -0,0 +1,45 @@
+
+
+
+ {#if h && showRoom}
+
+ Posted in #
+
+ {/if}
+
+
+ {#if showActivity}
+
+ {/if}
+
+
diff --git a/src/app/components/ClassifiedCreate.svelte b/src/app/components/ClassifiedCreate.svelte
new file mode 100644
index 000000000..bf0c44677
--- /dev/null
+++ b/src/app/components/ClassifiedCreate.svelte
@@ -0,0 +1,149 @@
+
+
+
+
+
+ Create a Classified Listing
+ Advertise a job, sale, or need.
+
+
+
+ {#snippet label()}
+ Title*
+ {/snippet}
+ {#snippet input()}
+
+ {/snippet}
+
+
+ {#snippet label()}
+ Description*
+ {/snippet}
+ {#snippet input()}
+
+
+
+ {/snippet}
+
+
+ {#snippet label()}
+ Price*
+ {/snippet}
+ {#snippet input()}
+ todo: value and search select inline
+ {/snippet}
+
+
+ {#snippet label()}
+ Images
+ {/snippet}
+ {#snippet input()}
+ todo: attach multiple images
+ {/snippet}
+
+
+
+
+
+
+
+
+
diff --git a/src/app/components/ClassifiedItem.svelte b/src/app/components/ClassifiedItem.svelte
new file mode 100644
index 000000000..60ddddbba
--- /dev/null
+++ b/src/app/components/ClassifiedItem.svelte
@@ -0,0 +1,49 @@
+
+
+
+ {#if title}
+
+
{title}
+
+ {formatTimestamp(event.created_at)}
+
+
+ {:else}
+
+ {formatTimestamp(event.created_at)}
+
+ {/if}
+
+
+
+ Posted by
+
+ {#if h}
+ in
+ {/if}
+
+
+
+
diff --git a/src/app/components/CommentActions.svelte b/src/app/components/CommentActions.svelte
index 6cffc5d59..c13d24244 100644
--- a/src/app/components/CommentActions.svelte
+++ b/src/app/components/CommentActions.svelte
@@ -5,19 +5,20 @@
import EventActivity from "@app/components/EventActivity.svelte"
import EventActions from "@app/components/EventActions.svelte"
import {publishDelete, publishReaction, canEnforceNip70} from "@app/core/commands"
- import {makeThreadPath} from "@app/util/routes"
+ import {makeSpacePath} from "@app/util/routes"
interface Props {
- url: any
- event: any
+ url: string
+ event: TrustedEvent
+ segment: string
showActivity?: boolean
}
- const {url, event, showActivity = false}: Props = $props()
+ const {url, event, segment, showActivity = false}: Props = $props()
const shouldProtect = canEnforceNip70(url)
- const path = makeThreadPath(url, event.id)
+ const path = makeSpacePath(url, segment, event.id)
const deleteReaction = async (event: TrustedEvent) =>
publishDelete({relays: [url], event, protect: await shouldProtect})
diff --git a/src/app/components/ComposeMenu.svelte b/src/app/components/ComposeMenu.svelte
index 26ec76a03..990728dec 100644
--- a/src/app/components/ComposeMenu.svelte
+++ b/src/app/components/ComposeMenu.svelte
@@ -3,11 +3,13 @@
import CalendarMinimalistic from "@assets/icons/calendar-minimalistic.svg?dataurl"
import StarFallMinimalistic from "@assets/icons/star-fall-minimalistic.svg?dataurl"
import NotesMinimalistic from "@assets/icons/notes-minimalistic.svg?dataurl"
+ import CaseMinimalistic from "@assets/icons/case-minimalistic.svg?dataurl"
import Button from "@lib/components/Button.svelte"
import Icon from "@lib/components/Icon.svelte"
import {pushModal} from "@app/util/modal"
import CalendarEventCreate from "@app/components/CalendarEventCreate.svelte"
import ThreadCreate from "@app/components/ThreadCreate.svelte"
+ import ClassifiedCreate from "@app/components/ClassifiedCreate.svelte"
import GoalCreate from "@app/components/GoalCreate.svelte"
type Props = {
@@ -24,6 +26,8 @@
const createThread = () => pushModal(ThreadCreate, {url, h})
+ const createClassified = () => pushModal(ClassifiedCreate, {url, h})
+
let ul: Element
onMount(() => {
@@ -35,13 +39,19 @@
+
+
+
diff --git a/src/app/components/EventInfo.svelte b/src/app/components/EventInfo.svelte
index 783b71aa7..59773f8b0 100644
--- a/src/app/components/EventInfo.svelte
+++ b/src/app/components/EventInfo.svelte
@@ -14,6 +14,7 @@
import Modal from "@lib/components/Modal.svelte"
import ModalBody from "@lib/components/ModalBody.svelte"
import ModalHeader from "@lib/components/ModalHeader.svelte"
+ import ModalFooter from "@lib/components/ModalFooter.svelte"
import ModalTitle from "@lib/components/ModalTitle.svelte"
import ModalSubtitle from "@lib/components/ModalSubtitle.svelte"
import {clip} from "@app/util/toast"
@@ -106,6 +107,8 @@
-
+
+
+
diff --git a/src/app/components/NoteContent.svelte b/src/app/components/NoteContent.svelte
index dc8597764..09d5ab13b 100644
--- a/src/app/components/NoteContent.svelte
+++ b/src/app/components/NoteContent.svelte
@@ -1,8 +1,9 @@
+
+
+ {#if title}
+
{title}
+ {/if}
+ {#if props.event.content}
+
+ {/if}
+
diff --git a/src/app/components/NoteContentMinimal.svelte b/src/app/components/NoteContentMinimal.svelte
index 179b478dc..7a1a5e8a9 100644
--- a/src/app/components/NoteContentMinimal.svelte
+++ b/src/app/components/NoteContentMinimal.svelte
@@ -1,8 +1,9 @@
+
+{#if title}
+ {title}
+{/if}
+{#if props.event.content}
+
+{/if}
diff --git a/src/app/components/RoomCompose.svelte b/src/app/components/RoomCompose.svelte
index 915b193f1..5f47549f0 100644
--- a/src/app/components/RoomCompose.svelte
+++ b/src/app/components/RoomCompose.svelte
@@ -131,8 +131,7 @@