Read thread, classified, article and goal titles, rooms and reports through their domain readers
This commit is contained in:
parent
945a9649e6
commit
66af2a201e
13 changed files with 51 additions and 52 deletions
|
|
@ -300,8 +300,7 @@ These predate the rule or are waiting on welshman. Don't copy them; fix one when
|
|||
relay lists in `SignUp.svelte` (`RelayList`, `MessagingRelayList`); deletes in
|
||||
`ProfileDelete.svelte` and the push adapter (`Delete`); the vanish request as a literal `62`
|
||||
(`VANISH`).
|
||||
- **Getter exists but bypassed:** titles in `src/app/title.ts`, `NoteContentThread` and the
|
||||
threads pages; calendar `start`/`end` in `src/app/feeds.ts` and the calendar page; poll `response`
|
||||
- **Getter exists but bypassed:** calendar `start`/`end` in `src/app/feeds.ts` and the calendar page; poll `response`
|
||||
tags in `PollVotes`; `p` tags of `RELAY_ADD_MEMBER`, `ROOM_ADD_MEMBER` and
|
||||
`ROOM_CREATE_PERMISSION` (`SpaceMembersSummary`, `RoomItemAddMember`, `src/app/management.ts`);
|
||||
comment `E`/`A` tags in the list pages and `src/app/classifieds.ts`; `imeta` unpacking in
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
<script lang="ts">
|
||||
import type {TrustedEvent} from "@welshman/util"
|
||||
import {getAddress, tagValue, tagSpec} from "@welshman/util"
|
||||
import {getAddress} from "@welshman/util"
|
||||
import {TimeEvent} from "@welshman/domain"
|
||||
import Link from "@lib/components/Link.svelte"
|
||||
import Cv from "@lib/components/Cv.svelte"
|
||||
import ContentActions from "@app/components/ContentActions.svelte"
|
||||
import CalendarEventEdit from "@app/components/CalendarEventEdit.svelte"
|
||||
import {reader} from "@app/core"
|
||||
import type {FeedContext} from "@app/feeds"
|
||||
import CalendarEventHeader from "@app/components/CalendarEventHeader.svelte"
|
||||
import ProfileLink from "@app/components/ProfileLink.svelte"
|
||||
|
|
@ -21,7 +23,7 @@
|
|||
const {url, event, context}: Props = $props()
|
||||
|
||||
const path = $derived(makeCalendarPath(url, getAddress(event)))
|
||||
const h = $derived(tagValue(tagSpec("h"), event.tags))
|
||||
const h = $derived(reader(TimeEvent)(event).room())
|
||||
</script>
|
||||
|
||||
<Cv tag={Link} class="relative flex flex-col gap-3 card card-interactive w-full" href={path}>
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
<script lang="ts">
|
||||
import type {ComponentProps} from "svelte"
|
||||
import {tagSpec, tagValue} from "@welshman/util"
|
||||
import {Thread} from "@welshman/domain"
|
||||
import ContentMinimal from "@app/components/ContentMinimal.svelte"
|
||||
import {reader} from "@app/core"
|
||||
|
||||
const props: ComponentProps<typeof ContentMinimal> = $props()
|
||||
|
||||
const title = tagValue(tagSpec("title"), props.event.tags)
|
||||
const title = reader(Thread)(props.event).title()
|
||||
</script>
|
||||
|
||||
{#if title}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
<script lang="ts">
|
||||
import type {ComponentProps} from "svelte"
|
||||
import {tagSpec, tagValue} from "@welshman/util"
|
||||
import {Thread} from "@welshman/domain"
|
||||
import Content from "@app/components/Content.svelte"
|
||||
import {reader} from "@app/core"
|
||||
|
||||
const props: ComponentProps<typeof Content> = $props()
|
||||
|
||||
const title = tagValue(tagSpec("title"), props.event.tags)
|
||||
const title = reader(Thread)(props.event).title()
|
||||
</script>
|
||||
|
||||
<div class="flex flex-col gap-2">
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
<script lang="ts">
|
||||
import type {TrustedEvent} from "@welshman/util"
|
||||
import {tagSpec, tagValue} from "@welshman/util"
|
||||
import {Poll} from "@welshman/domain"
|
||||
import Link from "@lib/components/Link.svelte"
|
||||
import Cv from "@lib/components/Cv.svelte"
|
||||
import NoteContent from "@app/components/NoteContent.svelte"
|
||||
import ContentActions from "@app/components/ContentActions.svelte"
|
||||
import {reader} from "@app/core"
|
||||
import type {FeedContext} from "@app/feeds"
|
||||
import RoomLink from "@app/components/RoomLink.svelte"
|
||||
import ProfileLink from "@app/components/ProfileLink.svelte"
|
||||
|
|
@ -20,7 +21,7 @@
|
|||
const {url, event, context}: Props = $props()
|
||||
|
||||
const path = makePollPath(url, event.id)
|
||||
const h = tagValue(tagSpec("h"), event.tags)
|
||||
const h = reader(Poll)(event).room()
|
||||
</script>
|
||||
|
||||
<Cv tag={Link} class="relative flex flex-col gap-2 card card-interactive w-full" href={path}>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<script lang="ts">
|
||||
import {onMount} from "svelte"
|
||||
import {matchTag, tagSpec} from "@welshman/util"
|
||||
import type {TrustedEvent} from "@welshman/util"
|
||||
import {Report} from "@welshman/domain"
|
||||
import InboxOut from "@assets/icons/inbox-out.svg?dataurl"
|
||||
import MinusCircle from "@assets/icons/minus-circle.svg?dataurl"
|
||||
import TrashBin2 from "@assets/icons/trash-bin-2.svg?dataurl"
|
||||
|
|
@ -9,7 +9,7 @@
|
|||
import Icon from "@lib/components/Icon.svelte"
|
||||
import Button from "@lib/components/Button.svelte"
|
||||
import Confirm from "@lib/components/Confirm.svelte"
|
||||
import {app, deletes, profiles, relayManagement, relays, user} from "@app/core"
|
||||
import {app, deletes, profiles, reader, relayManagement, relays, user} from "@app/core"
|
||||
import {deriveSpacePermissions} from "@app/management"
|
||||
import {pushToast} from "@app/toast"
|
||||
import {pushModal} from "@app/modal"
|
||||
|
|
@ -24,8 +24,10 @@
|
|||
const {url, event, onResolved, onClick}: Props = $props()
|
||||
|
||||
const permissions = deriveSpacePermissions(url)
|
||||
const etag = matchTag(tagSpec("e"), event.tags)
|
||||
const ptag = matchTag(tagSpec("p"), event.tags)
|
||||
const report = reader(Report)(event)
|
||||
const reportedId = report.eventId()
|
||||
const reportedPubkey = report.pubkey()
|
||||
const reason = report.reason() ?? ""
|
||||
|
||||
const deleteReport = async () => {
|
||||
const protect = await $relays.hasNip(url, 70)
|
||||
|
|
@ -48,20 +50,18 @@
|
|||
}
|
||||
|
||||
const banContent = () => {
|
||||
const [_, id, reason = ""] = etag!
|
||||
|
||||
pushModal(Confirm, {
|
||||
title: `Remove Content`,
|
||||
message: `Are you sure you want to delete this content from the space?`,
|
||||
confirm: async () => {
|
||||
const {error} = await $relayManagement.forUrl(url).banEvent(id, reason)
|
||||
const {error} = await $relayManagement.forUrl(url).banEvent(reportedId!, reason)
|
||||
|
||||
if (error) {
|
||||
pushToast({theme: "error", message: error})
|
||||
} else {
|
||||
pushToast({message: "Content has successfully been deleted!"})
|
||||
$app.repository.removeEvent(event.id)
|
||||
$app.repository.removeEvent(id)
|
||||
$app.repository.removeEvent(reportedId!)
|
||||
history.back()
|
||||
setTimeout(() => onResolved?.())
|
||||
}
|
||||
|
|
@ -70,13 +70,11 @@
|
|||
}
|
||||
|
||||
const banMember = () => {
|
||||
const [_, reported, reason = ""] = ptag!
|
||||
|
||||
pushModal(Confirm, {
|
||||
title: "Ban User",
|
||||
message: `Are you sure you want to ban @${$profiles.display(reported, [url]).get()} from the space?`,
|
||||
message: `Are you sure you want to ban @${$profiles.display(reportedPubkey!, [url]).get()} from the space?`,
|
||||
confirm: async () => {
|
||||
const {error} = await $relayManagement.forUrl(url).banPubkey(reported, reason)
|
||||
const {error} = await $relayManagement.forUrl(url).banPubkey(reportedPubkey!, reason)
|
||||
|
||||
if (error) {
|
||||
pushToast({theme: "error", message: error})
|
||||
|
|
@ -113,7 +111,7 @@
|
|||
Dismiss Report
|
||||
</Button>
|
||||
</li>
|
||||
{#if etag}
|
||||
{#if reportedId}
|
||||
<li>
|
||||
<Button class="text-error" onclick={banContent}>
|
||||
<Icon icon={TrashBin2} />
|
||||
|
|
@ -122,7 +120,7 @@
|
|||
</li>
|
||||
{/if}
|
||||
{/if}
|
||||
{#if ptag && $permissions.ban}
|
||||
{#if reportedPubkey && $permissions.ban}
|
||||
<li>
|
||||
<Button class="text-error" onclick={banMember}>
|
||||
<Icon icon={MinusCircle} />
|
||||
|
|
|
|||
|
|
@ -3,15 +3,8 @@
|
|||
import {readable} from "svelte/store"
|
||||
import {now, uniq, displayList, formatTimestampAsTime, formatTimestampAsDate} from "@welshman/lib"
|
||||
import type {TrustedEvent, EventContent} from "@welshman/util"
|
||||
import {
|
||||
MESSAGE,
|
||||
THREAD,
|
||||
getCommentFiltersForParent,
|
||||
getIdOrAddress,
|
||||
tagSpec,
|
||||
tagValue,
|
||||
} from "@welshman/util"
|
||||
import {getContentQuote} from "@welshman/domain"
|
||||
import {MESSAGE, THREAD, getCommentFiltersForParent, getIdOrAddress} from "@welshman/util"
|
||||
import {Thread, getContentQuote} from "@welshman/domain"
|
||||
import {isMobile} from "@lib/html"
|
||||
import NotesMinimalistic from "@assets/icons/notes-minimalistic.svg?dataurl"
|
||||
import Pen from "@assets/icons/pen.svg?dataurl"
|
||||
|
|
@ -32,7 +25,7 @@
|
|||
import RoomItemMenuButton from "@app/components/RoomItemMenuButton.svelte"
|
||||
import RoomItemMenuMobile from "@app/components/RoomItemMenuMobile.svelte"
|
||||
import RoomItemContent from "@app/components/RoomItemContent.svelte"
|
||||
import {events, profiles, thunks, user} from "@app/core"
|
||||
import {events, profiles, reader, thunks, user} from "@app/core"
|
||||
import {noThunks, thunksByEventId} from "@app/thunks"
|
||||
import {colorFor} from "@app/theme"
|
||||
import {ENABLE_ZAPS} from "@app/env"
|
||||
|
|
@ -154,7 +147,7 @@
|
|||
class="button button-xs button-neutral gap-1 rounded-full">
|
||||
<Icon icon={NotesMinimalistic} />
|
||||
<span class="max-w-48 truncate">
|
||||
{tagValue(tagSpec("title"), thread.tags) || "Thread"}
|
||||
{reader(Thread)(thread).title() || "Thread"}
|
||||
</span>
|
||||
</Link>
|
||||
{/each}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
<script lang="ts">
|
||||
import {tagSpec, tagValue} from "@welshman/util"
|
||||
import type {TrustedEvent} from "@welshman/util"
|
||||
import {RoomJoin} from "@welshman/domain"
|
||||
import Card from "@lib/components/Card.svelte"
|
||||
import Button from "@lib/components/Button.svelte"
|
||||
import ProfileName from "@app/components/ProfileName.svelte"
|
||||
import ProfileDetail from "@app/components/ProfileDetail.svelte"
|
||||
import RoomName from "@app/components/RoomName.svelte"
|
||||
import {app, relayManagement} from "@app/core"
|
||||
import {app, reader, relayManagement} from "@app/core"
|
||||
import {deriveSpacePermissions} from "@app/management"
|
||||
import {pushModal} from "@app/modal"
|
||||
import {pushToast} from "@app/toast"
|
||||
|
|
@ -20,7 +20,7 @@
|
|||
|
||||
const {url, event, onResolved}: Props = $props()
|
||||
|
||||
const h = tagValue(tagSpec("h"), event.tags) || ""
|
||||
const h = reader(RoomJoin)(event).room() || ""
|
||||
|
||||
const permissions = deriveSpacePermissions(url)
|
||||
|
||||
|
|
|
|||
|
|
@ -3,12 +3,14 @@
|
|||
import {derived} from "svelte/store"
|
||||
import {filter, formatTimestamp, max, spec} from "@welshman/lib"
|
||||
import type {TrustedEvent} from "@welshman/util"
|
||||
import {COMMENT, tagSpec, tagValue} from "@welshman/util"
|
||||
import {COMMENT} from "@welshman/util"
|
||||
import {Thread} from "@welshman/domain"
|
||||
import Link from "@lib/components/Link.svelte"
|
||||
import Cv from "@lib/components/Cv.svelte"
|
||||
import ProfileCircle from "@app/components/ProfileCircle.svelte"
|
||||
import ProfileName from "@app/components/ProfileName.svelte"
|
||||
import UnreadDot from "@app/components/UnreadDot.svelte"
|
||||
import {reader} from "@app/core"
|
||||
import type {FeedContext} from "@app/feeds"
|
||||
import {makeThreadPath} from "@app/routes"
|
||||
|
||||
|
|
@ -25,7 +27,7 @@
|
|||
const replies = derived(related, $related => filter(spec({kind: COMMENT}), $related))
|
||||
const replyCount = $derived($replies.length)
|
||||
const lastActive = $derived(max([...$replies, event].map(e => e.created_at)))
|
||||
const title = tagValue(tagSpec("title"), event.tags)
|
||||
const title = reader(Thread)(event).title()
|
||||
const path = makeThreadPath(url, event.id)
|
||||
const onClick = () => navigate(path)
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
<script lang="ts">
|
||||
import type {TrustedEvent} from "@welshman/util"
|
||||
import {tagSpec, tagValue} from "@welshman/util"
|
||||
import {Thread} from "@welshman/domain"
|
||||
import Button from "@lib/components/Button.svelte"
|
||||
import ProfileCircle from "@app/components/ProfileCircle.svelte"
|
||||
import ContentMinimal from "@app/components/ContentMinimal.svelte"
|
||||
import {reader} from "@app/core"
|
||||
|
||||
type Props = {
|
||||
url: string
|
||||
|
|
@ -13,7 +14,7 @@
|
|||
|
||||
const {url, event, onClick}: Props = $props()
|
||||
|
||||
const title = tagValue(tagSpec("title"), event.tags)
|
||||
const title = reader(Thread)(event).title()
|
||||
</script>
|
||||
|
||||
<Button
|
||||
|
|
|
|||
|
|
@ -37,9 +37,9 @@ export const AUDIO_CONTENT_TYPES = [
|
|||
]
|
||||
|
||||
export const getUrlTags = (url: string, event: TrustedEvent) => {
|
||||
// An imeta tag packs its own tags into space-separated values, so unpack them.
|
||||
// An imeta entry is "key value" and the value may contain spaces, so split at the first one only.
|
||||
const imetas = matchTags(tagSpec("imeta"), event.tags).map(([, ...values]: string[]) =>
|
||||
values.map(value => value.split(" ")),
|
||||
values.map(value => value.split(/ (.*)/s, 2)),
|
||||
)
|
||||
const imeta = imetas.find(meta => tagValue(tagSpec("url"), meta) === url)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,9 @@
|
|||
import {identity, uniq} from "@welshman/lib"
|
||||
import {tagSpec, tagValue} from "@welshman/util"
|
||||
import {displayPubkey} from "@welshman/domain"
|
||||
import {Article, Classified, Thread, ZapGoal, displayPubkey} from "@welshman/domain"
|
||||
import {makeRoomKey} from "@welshman/app"
|
||||
import {decodePubkey} from "@lib/util"
|
||||
import {getEventTitle} from "@app/calendar"
|
||||
import {app, profiles, rooms} from "@app/core"
|
||||
import {app, profiles, reader, rooms} from "@app/core"
|
||||
import {PLATFORM_NAME} from "@app/env"
|
||||
import {decodeRelay} from "@app/relays"
|
||||
|
||||
|
|
@ -134,7 +133,7 @@ export const getPageTitle = ({page, pubkey}: PageTitleContext) => {
|
|||
const event = getEventForTitle(routeId, page.params)
|
||||
|
||||
if (routeId === "/spaces/[relay]/threads/[id]") {
|
||||
return makeTitle(tagValue(tagSpec("title"), event?.tags || []) || "Thread")
|
||||
return makeTitle((event && reader(Thread)(event).title()) || "Thread")
|
||||
}
|
||||
|
||||
if (routeId === "/spaces/[relay]/calendar/[address]") {
|
||||
|
|
@ -142,15 +141,15 @@ export const getPageTitle = ({page, pubkey}: PageTitleContext) => {
|
|||
}
|
||||
|
||||
if (routeId === "/spaces/[relay]/classifieds/[address]") {
|
||||
return makeTitle(tagValue(tagSpec("title"), event?.tags || []) || "Listing")
|
||||
return makeTitle((event && reader(Classified)(event).title()) || "Listing")
|
||||
}
|
||||
|
||||
if (routeId === "/spaces/[relay]/articles/[address]") {
|
||||
return makeTitle(tagValue(tagSpec("title"), event?.tags || []) || "Article")
|
||||
return makeTitle((event && reader(Article)(event).title()) || "Article")
|
||||
}
|
||||
|
||||
if (routeId === "/spaces/[relay]/goals/[id]") {
|
||||
return makeTitle(event?.content || tagValue(tagSpec("summary"), event?.tags || []) || "Goal")
|
||||
return makeTitle(event?.content || (event && reader(ZapGoal)(event).summary()) || "Goal")
|
||||
}
|
||||
|
||||
return makeTitle()
|
||||
|
|
|
|||
|
|
@ -6,12 +6,14 @@
|
|||
import type {Maybe} from "@welshman/lib"
|
||||
import type {TrustedEvent} from "@welshman/util"
|
||||
import {THREAD, tagValue, tagSpec} from "@welshman/util"
|
||||
import {Thread} from "@welshman/domain"
|
||||
import NotesMinimalistic from "@assets/icons/notes-minimalistic.svg?dataurl"
|
||||
import Icon from "@lib/components/Icon.svelte"
|
||||
import PageContent from "@lib/components/PageContent.svelte"
|
||||
import Spinner from "@lib/components/Spinner.svelte"
|
||||
import SpaceBar from "@app/components/SpaceBar.svelte"
|
||||
import ThreadBoard from "@app/components/ThreadBoard.svelte"
|
||||
import {reader} from "@app/core"
|
||||
import {decodeRelay} from "@app/relays"
|
||||
import {displayRoom} from "@app/rooms"
|
||||
import {makeCommentFilter} from "@app/content"
|
||||
|
|
@ -46,7 +48,7 @@
|
|||
|
||||
const items = sortBy(e => -max([...(scores.get(e.id) || []), e.created_at]), threads)
|
||||
|
||||
const grouped = groupBy(e => tagValue(tagSpec("h"), e.tags) || "", items)
|
||||
const grouped = groupBy(e => reader(Thread)(e).room() || "", items)
|
||||
const byRoom = new Map<string, TrustedEvent[]>([["", []], ...grouped])
|
||||
const roomName = (h: string) => (h ? displayRoom(url, h) : "general").toLowerCase()
|
||||
const boards = sortBy(([h]) => roomName(h), Array.from(byRoom.entries()))
|
||||
|
|
|
|||
Loading…
Reference in a new issue