Add classified status

This commit is contained in:
Jon Staab
2026-02-03 17:09:30 -08:00
parent 815dbba497
commit 70c430ddc2
12 changed files with 80 additions and 31 deletions
@@ -1,6 +1,6 @@
<script lang="ts">
import type {TrustedEvent, EventContent} from "@welshman/util"
import {getTagValue} from "@welshman/util"
import {getTagValue, getAddress} from "@welshman/util"
import {pubkey} from "@welshman/app"
import Pen2 from "@assets/icons/pen-2.svg?dataurl"
import Icon from "@lib/components/Icon.svelte"
+1 -1
View File
@@ -1,6 +1,6 @@
<script lang="ts">
import type {TrustedEvent} from "@welshman/util"
import {getTagValue} from "@welshman/util"
import {getTagValue, getAddress} from "@welshman/util"
import Link from "@lib/components/Link.svelte"
import CalendarEventActions from "@app/components/CalendarEventActions.svelte"
import CalendarEventHeader from "@app/components/CalendarEventHeader.svelte"
+5 -2
View File
@@ -1,6 +1,6 @@
<script lang="ts">
import type {TrustedEvent, EventContent} from "@welshman/util"
import {getTagValue} from "@welshman/util"
import {getTagValue, getAddress} from "@welshman/util"
import {pubkey} from "@welshman/app"
import Pen2 from "@assets/icons/pen-2.svg?dataurl"
import Link from "@lib/components/Link.svelte"
@@ -8,6 +8,7 @@
import Button from "@lib/components/Button.svelte"
import RoomName from "@app/components/RoomName.svelte"
import ReactionSummary from "@app/components/ReactionSummary.svelte"
import ClassifiedStatus from "@app/components/ClassifiedStatus.svelte"
import ThunkStatusOrDeleted from "@app/components/ThunkStatusOrDeleted.svelte"
import EventActivity from "@app/components/EventActivity.svelte"
import EventActions from "@app/components/EventActions.svelte"
@@ -45,7 +46,9 @@
</Link>
{/if}
<ReactionSummary {url} {event} {deleteReaction} {createReaction} reactionClass="tooltip-left" />
<ThunkStatusOrDeleted {event} />
<ThunkStatusOrDeleted {event}>
<ClassifiedStatus {event} />
</ThunkStatusOrDeleted>
{#if showActivity}
<EventActivity {url} {path} {event} />
{/if}
+1 -1
View File
@@ -18,7 +18,7 @@
const {d, title, status} = fromPairs(event.tags)
const [_, price = 0, currency = "SAT"] = getTag("price", event.tags) || []
const images = getTagValues("image", event.tags)
const initialValues = {d, title, status, content, price, currency, images}
const initialValues = {d, title, status, content, price: Number(price), currency, images}
</script>
<ClassifiedForm {url} {initialValues}>
+2 -8
View File
@@ -9,9 +9,6 @@
import Field from "@lib/components/Field.svelte"
import Button from "@lib/components/Button.svelte"
import Spinner from "@lib/components/Spinner.svelte"
import ModalHeader from "@lib/components/ModalHeader.svelte"
import ModalTitle from "@lib/components/ModalTitle.svelte"
import ModalSubtitle from "@lib/components/ModalSubtitle.svelte"
import ModalFooter from "@lib/components/ModalFooter.svelte"
import Modal from "@lib/components/Modal.svelte"
import ModalBody from "@lib/components/ModalBody.svelte"
@@ -125,10 +122,7 @@
<Modal tag="form" onsubmit={preventDefault(submit)}>
<ModalBody>
<ModalHeader>
<ModalTitle>Create a Classified Listing</ModalTitle>
<ModalSubtitle>Advertise a job, sale, or need.</ModalSubtitle>
</ModalHeader>
{@render header?.()}
<div class="col-8 relative">
<Field>
{#snippet label()}
@@ -198,7 +192,7 @@
Go back
</Button>
<Button type="submit" class="btn btn-primary" disabled={loading}>
<Spinner {loading}>Create Listing</Spinner>
<Spinner {loading}>Save Listing</Spinner>
</Button>
</ModalFooter>
</Modal>
+7 -2
View File
@@ -1,8 +1,9 @@
<script lang="ts">
import {formatTimestamp} from "@welshman/lib"
import type {TrustedEvent} from "@welshman/util"
import {getTagValue, getTagValues} from "@welshman/util"
import {getTag, getAddress, getTagValue, getTagValues} from "@welshman/util"
import Link from "@lib/components/Link.svelte"
import CurrencySymbol from "@lib/components/CurrencySymbol.svelte"
import ContentLinkBlock from "@app/components/ContentLinkBlock.svelte"
import Content from "@app/components/Content.svelte"
import ProfileLink from "@app/components/ProfileLink.svelte"
@@ -20,6 +21,7 @@
const title = getTagValue("title", event.tags)
const h = getTagValue("h", event.tags)
const images = getTagValues("image", event.tags)
const [_, price = 0, currency = "SAT"] = getTag("price", event.tags) || []
</script>
<Link
@@ -27,7 +29,10 @@
href={makeClassifiedPath(url, getAddress(event))}>
{#if title}
<div class="flex w-full items-center justify-between gap-2">
<p class="text-xl">{title}</p>
<p class="text-xl">
{title}
<CurrencySymbol code={currency} />{price}
</p>
<p class="text-sm opacity-75">
{formatTimestamp(event.created_at)}
</p>
@@ -0,0 +1,20 @@
<script lang="ts">
import cx from "classnames"
import type {TrustedEvent} from "@welshman/util"
import {getTagValue} from "@welshman/util"
import {ucFirst} from "@lib/util"
type Props = {
event: TrustedEvent
}
const {event}: Props = $props()
const status = getTagValue("status", event.tags)
</script>
{#if status}
<div class={cx("btn btn-xs rounded-full", {"btn-primary": status !== "active"})}>
{ucFirst(status)}
</div>
{/if}
@@ -1,6 +1,7 @@
<script lang="ts">
import type {ComponentProps} from "svelte"
import {getTagValue, getTagValues} from "@welshman/util"
import {getTag, getTagValue, getTagValues} from "@welshman/util"
import CurrencySymbol from "@lib/components/CurrencySymbol.svelte"
import Content from "@app/components/Content.svelte"
import ContentLinkBlock from "@app/components/ContentLinkBlock.svelte"
@@ -8,11 +9,15 @@
const title = getTagValue("title", props.event.tags)
const images = getTagValues("image", props.event.tags)
const [_, price = 0, currency = "SAT"] = getTag("price", props.event.tags) || []
</script>
<div class="flex flex-col gap-2">
{#if title}
<p class="text-xl">{title}</p>
<p class="text-xl">
{title}
<CurrencySymbol code={currency} />{price}
</p>
{/if}
{#if props.event.content}
<Content {...props} />
+1 -1
View File
@@ -49,7 +49,7 @@
{count}
recent messages{count === 1 ? "" : "s"}
</span>
<Button class="btn btn-sm btn-primary" onclick={onClick}>
<Button class="btn btn-xs rounded-full btn-primary" onclick={onClick}>
View Conversation
<Icon icon={AltArrowRight} />
</Button>
+12 -3
View File
@@ -1,10 +1,17 @@
<script lang="ts">
import type {Snippet} from "svelte"
import type {TrustedEvent} from "@welshman/util"
import {PublishStatus} from "@welshman/net"
import {deriveIsDeleted} from "@welshman/store"
import {thunks, mergeThunks, repository} from "@welshman/app"
import {thunks, mergeThunks, thunkHasStatus, repository} from "@welshman/app"
import ThunkStatus from "@app/components/ThunkStatus.svelte"
const {event}: {event: TrustedEvent} = $props()
type Props = {
event: TrustedEvent
children?: Snippet
}
const {event, children}: Props = $props()
const deleted = deriveIsDeleted(repository, event)
const thunk = $derived(mergeThunks($thunks.filter(t => t.event.id === event.id)))
@@ -12,6 +19,8 @@
{#if $deleted}
<div class="btn btn-error btn-xs rounded-full">Deleted</div>
{:else if thunk}
{:else if thunk.thunks.length > 0 && !thunkHasStatus(PublishStatus.Success, thunk)}
<ThunkStatus {thunk} />
{:else if children}
{@render children?.()}
{/if}
+5 -3
View File
@@ -4,6 +4,7 @@ import * as nip19 from "nostr-tools/nip19"
import {goto} from "$app/navigation"
import {nthEq, sleep} from "@welshman/lib"
import type {TrustedEvent} from "@welshman/util"
import {getAddress} from "@welshman/util"
import {tracker, loadRelay} from "@welshman/app"
import {scrollToEvent} from "@lib/html"
import {identity} from "@welshman/lib"
@@ -66,9 +67,11 @@ export const makeGoalPath = (url: string, id?: string) => makeSpacePath(url, "go
export const makeThreadPath = (url: string, id?: string) => makeSpacePath(url, "threads", id)
export const makeClassifiedPath = (url: string, address?: string) => makeSpacePath(url, "classifieds", address)
export const makeClassifiedPath = (url: string, address?: string) =>
makeSpacePath(url, "classifieds", address)
export const makeCalendarPath = (url: string, address?: string) => makeSpacePath(url, "calendar", address)
export const makeCalendarPath = (url: string, address?: string) =>
makeSpacePath(url, "calendar", address)
export const getPrimaryNavItem = ($page: Page) => $page.route?.id?.split("/")[1]
@@ -160,7 +163,6 @@ export const getEventPath = async (event: TrustedEvent, urls: string[]) => {
if (parseInt(kind) === EVENT_TIME) {
return makeCalendarPath(url, address)
}
}
}
+18 -7
View File
@@ -10,6 +10,7 @@
ZAP_GOAL,
EVENT_TIME,
COMMENT,
getAddress,
getTagValue,
getTagValues,
getIdAndAddress,
@@ -26,6 +27,7 @@
import SpaceMenuButton from "@app/components/SpaceMenuButton.svelte"
import NoteItem from "@app/components/NoteItem.svelte"
import RecentConversation from "@app/components/RecentConversation.svelte"
import ClassifiedStatus from "@app/components/ClassifiedStatus.svelte"
import {decodeRelay, deriveEventsForUrl, CONTENT_KINDS} from "@app/core/state"
import {
makeThreadPath,
@@ -135,22 +137,31 @@
{:else}
<NoteItem {url} {event}>
{#if event.kind === THREAD}
<Link href={makeThreadPath(url, event.id)} class="btn btn-primary btn-sm">
<Link
href={makeThreadPath(url, event.id)}
class="btn btn-primary btn-xs rounded-full">
View Thread
<Icon icon={AltArrowRight} />
</Link>
{:else if event.kind === CLASSIFIED}
<Link href={makeClassifiedPath(url, getAddress(event))} class="btn btn-primary btn-sm">
View Listing
<Icon icon={AltArrowRight} />
</Link>
<div class="flex gap-2">
<ClassifiedStatus {event} />
<Link
href={makeClassifiedPath(url, getAddress(event))}
class="btn btn-primary btn-xs rounded-full">
View Listing
<Icon icon={AltArrowRight} />
</Link>
</div>
{:else if event.kind === ZAP_GOAL}
<Link href={makeGoalPath(url, event.id)} class="btn btn-primary btn-sm">
<Link href={makeGoalPath(url, event.id)} class="btn btn-primary btn-xs rounded-full">
View Goal
<Icon icon={AltArrowRight} />
</Link>
{:else if event.kind === EVENT_TIME}
<Link href={makeCalendarPath(url, getAddress(event))} class="btn btn-primary btn-sm">
<Link
href={makeCalendarPath(url, getAddress(event))}
class="btn btn-primary btn-xs rounded-full">
View Event
<Icon icon={AltArrowRight} />
</Link>