From 34cab50040096d10ae67c2a14ffc70ac82d9c465 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 23 Jan 2026 11:45:01 +0000 Subject: [PATCH] feat(nip34): Add status rendering to Patch and PR renderers - PatchRenderer and PatchDetailRenderer now show merge/closed/draft status - PullRequestRenderer and PullRequestDetailRenderer now show merge/closed/draft status - Status events fetched from repository relays with author outbox fallback - For patches and PRs, kind 1631 displays as "merged" instead of "resolved" - Fixed destructive color contrast in dark theme (30.6% -> 50% lightness) https://claude.ai/code/session_01C6Lty4k9pKxdwnYUCcpzV2 --- .../nostr/kinds/PatchDetailRenderer.tsx | 226 ++++++++++++++++-- src/components/nostr/kinds/PatchRenderer.tsx | 165 ++++++++++++- .../nostr/kinds/PullRequestDetailRenderer.tsx | 199 ++++++++++++++- .../nostr/kinds/PullRequestRenderer.tsx | 182 ++++++++++++-- src/index.css | 2 +- 5 files changed, 710 insertions(+), 64 deletions(-) diff --git a/src/components/nostr/kinds/PatchDetailRenderer.tsx b/src/components/nostr/kinds/PatchDetailRenderer.tsx index 8e32196..729e03b 100644 --- a/src/components/nostr/kinds/PatchDetailRenderer.tsx +++ b/src/components/nostr/kinds/PatchDetailRenderer.tsx @@ -1,5 +1,15 @@ import { useMemo } from "react"; -import { GitCommit, User, Copy, CopyCheck } from "lucide-react"; +import { + GitCommit, + User, + Copy, + CopyCheck, + CircleDot, + CheckCircle2, + XCircle, + FileEdit, + Loader2, +} from "lucide-react"; import { UserName } from "../UserName"; import { CodeCopyButton } from "@/components/CodeCopyButton"; import { useCopy } from "@/hooks/useCopy"; @@ -14,50 +24,189 @@ import { getPatchRepositoryAddress, isPatchRoot, isPatchRootRevision, + getRepositoryRelays, + getStatusType, + getValidStatusAuthors, + findCurrentStatus, } from "@/lib/nip34-helpers"; +import { parseReplaceableAddress } from "applesauce-core/helpers/pointers"; +import { getOutboxes } from "applesauce-core/helpers"; import { RepositoryLink } from "../RepositoryLink"; +import { useTimeline } from "@/hooks/useTimeline"; +import { useNostrEvent } from "@/hooks/useNostrEvent"; +import { AGGREGATOR_RELAYS } from "@/services/loaders"; + +/** + * Get the icon for a status kind + */ +function getStatusIcon(kind: number) { + switch (kind) { + case 1630: + return CircleDot; + case 1631: + return CheckCircle2; + case 1632: + return XCircle; + case 1633: + return FileEdit; + default: + return CircleDot; + } +} + +/** + * Get the color classes for a status badge + * Uses theme semantic colors + */ +function getStatusBadgeClasses(kind: number): string { + switch (kind) { + case 1630: // Open - neutral + return "bg-muted/50 text-foreground border-border"; + case 1631: // Merged - positive + return "bg-accent/20 text-accent border-accent/30"; + case 1632: // Closed - negative + return "bg-destructive/20 text-destructive border-destructive/30"; + case 1633: // Draft - muted + return "bg-muted text-muted-foreground border-muted-foreground/30"; + default: + return "bg-muted/50 text-foreground border-border"; + } +} /** * Detail renderer for Kind 1617 - Patch - * Displays full patch metadata and content + * Displays full patch metadata and content with status */ export function PatchDetailRenderer({ event }: { event: NostrEvent }) { const { copy, copied } = useCopy(); - const subject = useMemo(() => getPatchSubject(event), [event]); - const commitId = useMemo(() => getPatchCommitId(event), [event]); - const parentCommit = useMemo(() => getPatchParentCommit(event), [event]); - const committer = useMemo(() => getPatchCommitter(event), [event]); - const repoAddress = useMemo(() => getPatchRepositoryAddress(event), [event]); - const isRoot = useMemo(() => isPatchRoot(event), [event]); - const isRootRevision = useMemo(() => isPatchRootRevision(event), [event]); + const subject = getPatchSubject(event); + const commitId = getPatchCommitId(event); + const parentCommit = getPatchParentCommit(event); + const committer = getPatchCommitter(event); + const repoAddress = getPatchRepositoryAddress(event); + const isRoot = isPatchRoot(event); + const isRootRevision = isPatchRootRevision(event); + + // Parse repository address for fetching repo event + const parsedRepo = useMemo( + () => (repoAddress ? parseReplaceableAddress(repoAddress) : null), + [repoAddress], + ); + + // Fetch repository event to get maintainers list + const repoPointer = useMemo(() => { + if (!parsedRepo) return undefined; + return { + kind: parsedRepo.kind, + pubkey: parsedRepo.pubkey, + identifier: parsedRepo.identifier, + }; + }, [parsedRepo]); + + const repositoryEvent = useNostrEvent(repoPointer); + + // Fetch repo author's relay list for fallback + const repoAuthorRelayListPointer = useMemo(() => { + if (!parsedRepo?.pubkey) return undefined; + return { kind: 10002, pubkey: parsedRepo.pubkey, identifier: "" }; + }, [parsedRepo?.pubkey]); + + const repoAuthorRelayList = useNostrEvent(repoAuthorRelayListPointer); + + // Build relay list with fallbacks + const statusRelays = useMemo(() => { + if (repositoryEvent) { + const repoRelays = getRepositoryRelays(repositoryEvent); + if (repoRelays.length > 0) return repoRelays; + } + if (repoAuthorRelayList) { + const authorOutbox = getOutboxes(repoAuthorRelayList); + if (authorOutbox.length > 0) return authorOutbox; + } + return AGGREGATOR_RELAYS; + }, [repositoryEvent, repoAuthorRelayList]); + + // Fetch status events + const statusFilter = useMemo( + () => ({ + kinds: [1630, 1631, 1632, 1633], + "#e": [event.id], + }), + [event.id], + ); + + const { events: statusEvents, loading: statusLoading } = useTimeline( + `patch-status-${event.id}`, + statusFilter, + statusRelays, + { limit: 20 }, + ); + + // Get valid status authors + const validAuthors = useMemo( + () => getValidStatusAuthors(event, repositoryEvent), + [event, repositoryEvent], + ); + + // Get the most recent valid status event + const currentStatus = useMemo( + () => findCurrentStatus(statusEvents, validAuthors), + [statusEvents, validAuthors], + ); // Format created date using locale utility const createdDate = formatTimestamp(event.created_at, "long"); + // Status display - for patches, 1631 means "merged" + const statusType = currentStatus + ? currentStatus.kind === 1631 + ? "merged" + : getStatusType(currentStatus.kind) + : "open"; + const StatusIcon = currentStatus + ? getStatusIcon(currentStatus.kind) + : CircleDot; + const statusBadgeClasses = currentStatus + ? getStatusBadgeClasses(currentStatus.kind) + : "bg-muted/50 text-foreground border-border"; + return (
{/* Patch Header */}
+ {/* Status Badge */} +
+ {statusLoading ? ( + + + Loading status... + + ) : ( + + + {statusType} + + )} + + {/* Root badges */} + {isRoot && ( + + Root Patch + + )} + {isRootRevision && ( + + Root Revision + + )} +
+ {/* Title */}

{subject || "Untitled Patch"}

- {/* Status Badges */} - {(isRoot || isRootRevision) && ( -
- {isRoot && ( - - Root Patch - - )} - {isRootRevision && ( - - Root Revision - - )} -
- )} - {/* Repository Link */} {repoAddress && (
@@ -169,6 +318,33 @@ export function PatchDetailRenderer({ event }: { event: NostrEvent }) {
)} + + {/* Status History */} + {currentStatus && ( +
+

+ Last Status Update +

+
+ + + {currentStatus.kind === 1631 + ? "merged" + : getStatusType(currentStatus.kind) || "updated"}{" "} + this patch + + + +
+ {currentStatus.content && ( +

+ {currentStatus.content} +

+ )} +
+ )}
); } diff --git a/src/components/nostr/kinds/PatchRenderer.tsx b/src/components/nostr/kinds/PatchRenderer.tsx index 3f4620d..e29fde8 100644 --- a/src/components/nostr/kinds/PatchRenderer.tsx +++ b/src/components/nostr/kinds/PatchRenderer.tsx @@ -1,3 +1,5 @@ +import { useMemo } from "react"; +import { CircleDot, CheckCircle2, XCircle, FileEdit } from "lucide-react"; import { BaseEventContainer, type BaseEventProps, @@ -7,12 +9,58 @@ import { getPatchSubject, getPatchCommitId, getPatchRepositoryAddress, + getRepositoryRelays, + getStatusType, + getValidStatusAuthors, + findCurrentStatus, } from "@/lib/nip34-helpers"; +import { parseReplaceableAddress } from "applesauce-core/helpers/pointers"; +import { getOutboxes } from "applesauce-core/helpers"; import { RepositoryLink } from "../RepositoryLink"; +import { useTimeline } from "@/hooks/useTimeline"; +import { useNostrEvent } from "@/hooks/useNostrEvent"; +import { AGGREGATOR_RELAYS } from "@/services/loaders"; + +/** + * Get the icon for a status kind + */ +function getStatusIcon(kind: number) { + switch (kind) { + case 1630: + return CircleDot; + case 1631: + return CheckCircle2; + case 1632: + return XCircle; + case 1633: + return FileEdit; + default: + return CircleDot; + } +} + +/** + * Get the color class for a status kind + * Uses theme semantic colors + */ +function getStatusColorClass(kind: number): string { + switch (kind) { + case 1630: // Open - neutral + return "text-foreground"; + case 1631: // Merged - positive + return "text-accent"; + case 1632: // Closed - negative + return "text-destructive"; + case 1633: // Draft - muted + return "text-muted-foreground"; + default: + return "text-foreground"; + } +} /** * Renderer for Kind 1617 - Patch - * Displays as a compact patch card in feed view + * Displays as a compact patch card in feed view with status */ export function PatchRenderer({ event }: BaseEventProps) { const subject = getPatchSubject(event); @@ -22,22 +70,117 @@ export function PatchRenderer({ event }: BaseEventProps) { // Shorten commit ID for display const shortCommitId = commitId ? commitId.slice(0, 7) : undefined; + // Parse repository address for fetching repo event + const parsedRepo = useMemo( + () => (repoAddress ? parseReplaceableAddress(repoAddress) : null), + [repoAddress], + ); + + // Fetch repository event to get maintainers list + const repoPointer = useMemo(() => { + if (!parsedRepo) return undefined; + return { + kind: parsedRepo.kind, + pubkey: parsedRepo.pubkey, + identifier: parsedRepo.identifier, + }; + }, [parsedRepo]); + + const repositoryEvent = useNostrEvent(repoPointer); + + // Fetch repo author's relay list for fallback + const repoAuthorRelayListPointer = useMemo(() => { + if (!parsedRepo?.pubkey) return undefined; + return { kind: 10002, pubkey: parsedRepo.pubkey, identifier: "" }; + }, [parsedRepo?.pubkey]); + + const repoAuthorRelayList = useNostrEvent(repoAuthorRelayListPointer); + + // Build relay list with fallbacks: + // 1. Repository configured relays + // 2. Repo author's outbox (write) relays + // 3. AGGREGATOR_RELAYS as final fallback + const statusRelays = useMemo(() => { + // Try repository relays first + if (repositoryEvent) { + const repoRelays = getRepositoryRelays(repositoryEvent); + if (repoRelays.length > 0) return repoRelays; + } + + // Try repo author's outbox relays + if (repoAuthorRelayList) { + const authorOutbox = getOutboxes(repoAuthorRelayList); + if (authorOutbox.length > 0) return authorOutbox; + } + + // Fallback to aggregator relays + return AGGREGATOR_RELAYS; + }, [repositoryEvent, repoAuthorRelayList]); + + // Fetch status events that reference this patch + const statusFilter = useMemo( + () => ({ + kinds: [1630, 1631, 1632, 1633], + "#e": [event.id], + }), + [event.id], + ); + + const { events: statusEvents } = useTimeline( + `patch-status-${event.id}`, + statusFilter, + statusRelays, + { limit: 10 }, + ); + + // Get valid status authors (patch author + repo owner + maintainers) + const validAuthors = useMemo( + () => getValidStatusAuthors(event, repositoryEvent), + [event, repositoryEvent], + ); + + // Get the most recent valid status event + const currentStatus = useMemo( + () => findCurrentStatus(statusEvents, validAuthors), + [statusEvents, validAuthors], + ); + + // Status display - for patches, 1631 means "merged" not "resolved" + const statusType = currentStatus + ? currentStatus.kind === 1631 + ? "merged" + : getStatusType(currentStatus.kind) + : "open"; + const StatusIcon = currentStatus + ? getStatusIcon(currentStatus.kind) + : CircleDot; + const statusColorClass = currentStatus + ? getStatusColorClass(currentStatus.kind) + : "text-foreground"; + return (
- {/* Patch Subject */} - - {subject || "Untitled Patch"} - + {/* Status and Subject */} +
+ + + {subject || "Untitled Patch"} + +
{/* Metadata */}
- in - {/* Repository */} - {repoAddress && } + {statusType} + {repoAddress && ( + <> + in + + + )} {/* Commit ID */} {shortCommitId && ( diff --git a/src/components/nostr/kinds/PullRequestDetailRenderer.tsx b/src/components/nostr/kinds/PullRequestDetailRenderer.tsx index 89027b1..55d7afd 100644 --- a/src/components/nostr/kinds/PullRequestDetailRenderer.tsx +++ b/src/components/nostr/kinds/PullRequestDetailRenderer.tsx @@ -1,5 +1,15 @@ import { useMemo } from "react"; -import { GitBranch, Tag, Copy, CopyCheck } from "lucide-react"; +import { + GitBranch, + Tag, + Copy, + CopyCheck, + CircleDot, + CheckCircle2, + XCircle, + FileEdit, + Loader2, +} from "lucide-react"; import { UserName } from "../UserName"; import { MarkdownContent } from "../MarkdownContent"; import { useCopy } from "@/hooks/useCopy"; @@ -13,35 +23,175 @@ import { getPullRequestCloneUrls, getPullRequestMergeBase, getPullRequestRepositoryAddress, + getRepositoryRelays, + getStatusType, + getValidStatusAuthors, + findCurrentStatus, } from "@/lib/nip34-helpers"; +import { parseReplaceableAddress } from "applesauce-core/helpers/pointers"; +import { getOutboxes } from "applesauce-core/helpers"; import { Label } from "@/components/ui/label"; import { RepositoryLink } from "../RepositoryLink"; +import { useTimeline } from "@/hooks/useTimeline"; +import { useNostrEvent } from "@/hooks/useNostrEvent"; +import { AGGREGATOR_RELAYS } from "@/services/loaders"; + +/** + * Get the icon for a status kind + */ +function getStatusIcon(kind: number) { + switch (kind) { + case 1630: + return CircleDot; + case 1631: + return CheckCircle2; + case 1632: + return XCircle; + case 1633: + return FileEdit; + default: + return CircleDot; + } +} + +/** + * Get the color classes for a status badge + * Uses theme semantic colors + */ +function getStatusBadgeClasses(kind: number): string { + switch (kind) { + case 1630: // Open - neutral + return "bg-muted/50 text-foreground border-border"; + case 1631: // Merged - positive + return "bg-accent/20 text-accent border-accent/30"; + case 1632: // Closed - negative + return "bg-destructive/20 text-destructive border-destructive/30"; + case 1633: // Draft - muted + return "bg-muted text-muted-foreground border-muted-foreground/30"; + default: + return "bg-muted/50 text-foreground border-border"; + } +} /** * Detail renderer for Kind 1618 - Pull Request - * Displays full PR content with markdown rendering + * Displays full PR content with markdown rendering and status */ export function PullRequestDetailRenderer({ event }: { event: NostrEvent }) { const { copy, copied } = useCopy(); - const subject = useMemo(() => getPullRequestSubject(event), [event]); - const labels = useMemo(() => getPullRequestLabels(event), [event]); - const commitId = useMemo(() => getPullRequestCommitId(event), [event]); - const branchName = useMemo(() => getPullRequestBranchName(event), [event]); - const cloneUrls = useMemo(() => getPullRequestCloneUrls(event), [event]); - const mergeBase = useMemo(() => getPullRequestMergeBase(event), [event]); - const repoAddress = useMemo( - () => getPullRequestRepositoryAddress(event), - [event], + const subject = getPullRequestSubject(event); + const labels = getPullRequestLabels(event); + const commitId = getPullRequestCommitId(event); + const branchName = getPullRequestBranchName(event); + const cloneUrls = getPullRequestCloneUrls(event); + const mergeBase = getPullRequestMergeBase(event); + const repoAddress = getPullRequestRepositoryAddress(event); + + // Parse repository address for fetching repo event + const parsedRepo = useMemo( + () => (repoAddress ? parseReplaceableAddress(repoAddress) : null), + [repoAddress], + ); + + // Fetch repository event to get maintainers list + const repoPointer = useMemo(() => { + if (!parsedRepo) return undefined; + return { + kind: parsedRepo.kind, + pubkey: parsedRepo.pubkey, + identifier: parsedRepo.identifier, + }; + }, [parsedRepo]); + + const repositoryEvent = useNostrEvent(repoPointer); + + // Fetch repo author's relay list for fallback + const repoAuthorRelayListPointer = useMemo(() => { + if (!parsedRepo?.pubkey) return undefined; + return { kind: 10002, pubkey: parsedRepo.pubkey, identifier: "" }; + }, [parsedRepo?.pubkey]); + + const repoAuthorRelayList = useNostrEvent(repoAuthorRelayListPointer); + + // Build relay list with fallbacks + const statusRelays = useMemo(() => { + if (repositoryEvent) { + const repoRelays = getRepositoryRelays(repositoryEvent); + if (repoRelays.length > 0) return repoRelays; + } + if (repoAuthorRelayList) { + const authorOutbox = getOutboxes(repoAuthorRelayList); + if (authorOutbox.length > 0) return authorOutbox; + } + return AGGREGATOR_RELAYS; + }, [repositoryEvent, repoAuthorRelayList]); + + // Fetch status events + const statusFilter = useMemo( + () => ({ + kinds: [1630, 1631, 1632, 1633], + "#e": [event.id], + }), + [event.id], + ); + + const { events: statusEvents, loading: statusLoading } = useTimeline( + `pr-status-${event.id}`, + statusFilter, + statusRelays, + { limit: 20 }, + ); + + // Get valid status authors + const validAuthors = useMemo( + () => getValidStatusAuthors(event, repositoryEvent), + [event, repositoryEvent], + ); + + // Get the most recent valid status event + const currentStatus = useMemo( + () => findCurrentStatus(statusEvents, validAuthors), + [statusEvents, validAuthors], ); // Format created date using locale utility const createdDate = formatTimestamp(event.created_at, "long"); + // Status display - for PRs, 1631 means "merged" + const statusType = currentStatus + ? currentStatus.kind === 1631 + ? "merged" + : getStatusType(currentStatus.kind) + : "open"; + const StatusIcon = currentStatus + ? getStatusIcon(currentStatus.kind) + : CircleDot; + const statusBadgeClasses = currentStatus + ? getStatusBadgeClasses(currentStatus.kind) + : "bg-muted/50 text-foreground border-border"; + return (
{/* PR Header */}
+ {/* Status Badge */} +
+ {statusLoading ? ( + + + Loading status... + + ) : ( + + + {statusType} + + )} +
+ {/* Title */}

{subject || "Untitled Pull Request"} @@ -195,6 +345,33 @@ export function PullRequestDetailRenderer({ event }: { event: NostrEvent }) { (No description provided)

)} + + {/* Status History */} + {currentStatus && ( +
+

+ Last Status Update +

+
+ + + {currentStatus.kind === 1631 + ? "merged" + : getStatusType(currentStatus.kind) || "updated"}{" "} + this pull request + + + +
+ {currentStatus.content && ( +

+ {currentStatus.content} +

+ )} +
+ )}

); } diff --git a/src/components/nostr/kinds/PullRequestRenderer.tsx b/src/components/nostr/kinds/PullRequestRenderer.tsx index 97ab93e..38aff45 100644 --- a/src/components/nostr/kinds/PullRequestRenderer.tsx +++ b/src/components/nostr/kinds/PullRequestRenderer.tsx @@ -1,21 +1,74 @@ +import { useMemo } from "react"; +import { + CircleDot, + CheckCircle2, + XCircle, + FileEdit, + GitBranch, +} from "lucide-react"; import { BaseEventContainer, type BaseEventProps, ClickableEventTitle, } from "./BaseEventRenderer"; -import { GitBranch } from "lucide-react"; import { getPullRequestSubject, getPullRequestLabels, getPullRequestBranchName, getPullRequestRepositoryAddress, + getRepositoryRelays, + getStatusType, + getValidStatusAuthors, + findCurrentStatus, } from "@/lib/nip34-helpers"; +import { parseReplaceableAddress } from "applesauce-core/helpers/pointers"; +import { getOutboxes } from "applesauce-core/helpers"; import { Label } from "@/components/ui/label"; import { RepositoryLink } from "../RepositoryLink"; +import { useTimeline } from "@/hooks/useTimeline"; +import { useNostrEvent } from "@/hooks/useNostrEvent"; +import { AGGREGATOR_RELAYS } from "@/services/loaders"; + +/** + * Get the icon for a status kind + */ +function getStatusIcon(kind: number) { + switch (kind) { + case 1630: + return CircleDot; + case 1631: + return CheckCircle2; + case 1632: + return XCircle; + case 1633: + return FileEdit; + default: + return CircleDot; + } +} + +/** + * Get the color class for a status kind + * Uses theme semantic colors + */ +function getStatusColorClass(kind: number): string { + switch (kind) { + case 1630: // Open - neutral + return "text-foreground"; + case 1631: // Merged - positive + return "text-accent"; + case 1632: // Closed - negative + return "text-destructive"; + case 1633: // Draft - muted + return "text-muted-foreground"; + default: + return "text-foreground"; + } +} /** * Renderer for Kind 1618 - Pull Request - * Displays as a compact PR card in feed view + * Displays as a compact PR card in feed view with status */ export function PullRequestRenderer({ event }: BaseEventProps) { const subject = getPullRequestSubject(event); @@ -23,25 +76,122 @@ export function PullRequestRenderer({ event }: BaseEventProps) { const branchName = getPullRequestBranchName(event); const repoAddress = getPullRequestRepositoryAddress(event); + // Parse repository address for fetching repo event + const parsedRepo = useMemo( + () => (repoAddress ? parseReplaceableAddress(repoAddress) : null), + [repoAddress], + ); + + // Fetch repository event to get maintainers list + const repoPointer = useMemo(() => { + if (!parsedRepo) return undefined; + return { + kind: parsedRepo.kind, + pubkey: parsedRepo.pubkey, + identifier: parsedRepo.identifier, + }; + }, [parsedRepo]); + + const repositoryEvent = useNostrEvent(repoPointer); + + // Fetch repo author's relay list for fallback + const repoAuthorRelayListPointer = useMemo(() => { + if (!parsedRepo?.pubkey) return undefined; + return { kind: 10002, pubkey: parsedRepo.pubkey, identifier: "" }; + }, [parsedRepo?.pubkey]); + + const repoAuthorRelayList = useNostrEvent(repoAuthorRelayListPointer); + + // Build relay list with fallbacks: + // 1. Repository configured relays + // 2. Repo author's outbox (write) relays + // 3. AGGREGATOR_RELAYS as final fallback + const statusRelays = useMemo(() => { + // Try repository relays first + if (repositoryEvent) { + const repoRelays = getRepositoryRelays(repositoryEvent); + if (repoRelays.length > 0) return repoRelays; + } + + // Try repo author's outbox relays + if (repoAuthorRelayList) { + const authorOutbox = getOutboxes(repoAuthorRelayList); + if (authorOutbox.length > 0) return authorOutbox; + } + + // Fallback to aggregator relays + return AGGREGATOR_RELAYS; + }, [repositoryEvent, repoAuthorRelayList]); + + // Fetch status events that reference this PR + const statusFilter = useMemo( + () => ({ + kinds: [1630, 1631, 1632, 1633], + "#e": [event.id], + }), + [event.id], + ); + + const { events: statusEvents } = useTimeline( + `pr-status-${event.id}`, + statusFilter, + statusRelays, + { limit: 10 }, + ); + + // Get valid status authors (PR author + repo owner + maintainers) + const validAuthors = useMemo( + () => getValidStatusAuthors(event, repositoryEvent), + [event, repositoryEvent], + ); + + // Get the most recent valid status event + const currentStatus = useMemo( + () => findCurrentStatus(statusEvents, validAuthors), + [statusEvents, validAuthors], + ); + + // Status display - for PRs, 1631 means "merged" not "resolved" + const statusType = currentStatus + ? currentStatus.kind === 1631 + ? "merged" + : getStatusType(currentStatus.kind) + : "open"; + const StatusIcon = currentStatus + ? getStatusIcon(currentStatus.kind) + : CircleDot; + const statusColorClass = currentStatus + ? getStatusColorClass(currentStatus.kind) + : "text-foreground"; + return (
- {/* PR Title */} - - {subject || "Untitled Pull Request"} - + {/* Status and PR Title */} +
+ + + {subject || "Untitled Pull Request"} + +
- {/* Repository */} - {repoAddress && ( - - )} + {/* Status and Repository */} +
+ {statusType} + {repoAddress && ( + <> + in + + + )} +
{/* Branch Name */} {branchName && (
diff --git a/src/index.css b/src/index.css index 611f2c8..5d7637e 100644 --- a/src/index.css +++ b/src/index.css @@ -115,7 +115,7 @@ --muted-foreground: 215 20.2% 70%; --accent: 270 100% 70%; --accent-foreground: 222.2 84% 4.9%; - --destructive: 0 62.8% 30.6%; + --destructive: 0 72% 50%; --destructive-foreground: 210 40% 98%; --border: 217.2 32.6% 17.5%; --input: 217.2 32.6% 17.5%;