mirror of
https://github.com/purrgrammer/grimoire.git
synced 2026-04-09 23:16:50 +02:00
refactor: extract useRepositoryRelays hook from NIP-34 renderers
Removes ~45 lines of identical relay resolution boilerplate duplicated across 6 renderers (Issue, Patch, PR - feed and detail views). The hook encapsulates the 3-tier fallback: repo relays → owner outbox → aggregators, and also returns the repository event needed for getValidStatusAuthors. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -7,20 +7,16 @@ import {
|
||||
getIssueTitle,
|
||||
getIssueLabels,
|
||||
getIssueRepositoryAddress,
|
||||
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 { StatusIndicator } from "../StatusIndicator";
|
||||
import { useTimeline } from "@/hooks/useTimeline";
|
||||
import { useNostrEvent } from "@/hooks/useNostrEvent";
|
||||
import { formatTimestamp } from "@/hooks/useLocale";
|
||||
import { AGGREGATOR_RELAYS } from "@/services/loaders";
|
||||
import { useRepositoryRelays } from "@/hooks/useRepositoryRelays";
|
||||
|
||||
/**
|
||||
* Detail renderer for Kind 1621 - Issue (NIP-34)
|
||||
@@ -31,52 +27,8 @@ export function IssueDetailRenderer({ event }: { event: NostrEvent }) {
|
||||
const labels = getIssueLabels(event);
|
||||
const repoAddress = getIssueRepositoryAddress(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]);
|
||||
const { relays: statusRelays, repositoryEvent } =
|
||||
useRepositoryRelays(repoAddress);
|
||||
|
||||
// Fetch status events that reference this issue
|
||||
// Status events use e tag with root marker to reference the issue
|
||||
|
||||
@@ -8,18 +8,14 @@ import {
|
||||
getIssueTitle,
|
||||
getIssueLabels,
|
||||
getIssueRepositoryAddress,
|
||||
getRepositoryRelays,
|
||||
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 { StatusIndicator } from "../StatusIndicator";
|
||||
import { useTimeline } from "@/hooks/useTimeline";
|
||||
import { useNostrEvent } from "@/hooks/useNostrEvent";
|
||||
import { AGGREGATOR_RELAYS } from "@/services/loaders";
|
||||
import { useRepositoryRelays } from "@/hooks/useRepositoryRelays";
|
||||
|
||||
/**
|
||||
* Renderer for Kind 1621 - Issue
|
||||
@@ -30,52 +26,8 @@ export function IssueRenderer({ event }: BaseEventProps) {
|
||||
const labels = getIssueLabels(event);
|
||||
const repoAddress = getIssueRepositoryAddress(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]);
|
||||
const { relays: statusRelays, repositoryEvent } =
|
||||
useRepositoryRelays(repoAddress);
|
||||
|
||||
// Fetch status events that reference this issue
|
||||
const statusFilter = useMemo(
|
||||
|
||||
@@ -15,18 +15,14 @@ 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 { StatusIndicator } from "../StatusIndicator";
|
||||
import { useTimeline } from "@/hooks/useTimeline";
|
||||
import { useNostrEvent } from "@/hooks/useNostrEvent";
|
||||
import { AGGREGATOR_RELAYS } from "@/services/loaders";
|
||||
import { useRepositoryRelays } from "@/hooks/useRepositoryRelays";
|
||||
|
||||
/**
|
||||
* Detail renderer for Kind 1617 - Patch
|
||||
@@ -43,44 +39,8 @@ export function PatchDetailRenderer({ event }: { event: NostrEvent }) {
|
||||
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]);
|
||||
const { relays: statusRelays, repositoryEvent } =
|
||||
useRepositoryRelays(repoAddress);
|
||||
|
||||
// Fetch status events
|
||||
const statusFilter = useMemo(
|
||||
|
||||
@@ -8,17 +8,13 @@ import {
|
||||
getPatchSubject,
|
||||
getPatchCommitId,
|
||||
getPatchRepositoryAddress,
|
||||
getRepositoryRelays,
|
||||
getValidStatusAuthors,
|
||||
findCurrentStatus,
|
||||
} from "@/lib/nip34-helpers";
|
||||
import { parseReplaceableAddress } from "applesauce-core/helpers/pointers";
|
||||
import { getOutboxes } from "applesauce-core/helpers";
|
||||
import { RepositoryLink } from "../RepositoryLink";
|
||||
import { StatusIndicator } from "../StatusIndicator";
|
||||
import { useTimeline } from "@/hooks/useTimeline";
|
||||
import { useNostrEvent } from "@/hooks/useNostrEvent";
|
||||
import { AGGREGATOR_RELAYS } from "@/services/loaders";
|
||||
import { useRepositoryRelays } from "@/hooks/useRepositoryRelays";
|
||||
|
||||
/**
|
||||
* Renderer for Kind 1617 - Patch
|
||||
@@ -32,52 +28,8 @@ 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]);
|
||||
const { relays: statusRelays, repositoryEvent } =
|
||||
useRepositoryRelays(repoAddress);
|
||||
|
||||
// Fetch status events that reference this patch
|
||||
const statusFilter = useMemo(
|
||||
|
||||
@@ -13,19 +13,15 @@ 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 { StatusIndicator } from "../StatusIndicator";
|
||||
import { useTimeline } from "@/hooks/useTimeline";
|
||||
import { useNostrEvent } from "@/hooks/useNostrEvent";
|
||||
import { AGGREGATOR_RELAYS } from "@/services/loaders";
|
||||
import { useRepositoryRelays } from "@/hooks/useRepositoryRelays";
|
||||
|
||||
/**
|
||||
* Detail renderer for Kind 1618 - Pull Request
|
||||
@@ -42,44 +38,8 @@ export function PullRequestDetailRenderer({ event }: { event: NostrEvent }) {
|
||||
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]);
|
||||
const { relays: statusRelays, repositoryEvent } =
|
||||
useRepositoryRelays(repoAddress);
|
||||
|
||||
// Fetch status events
|
||||
const statusFilter = useMemo(
|
||||
|
||||
@@ -10,18 +10,14 @@ import {
|
||||
getPullRequestLabels,
|
||||
getPullRequestBranchName,
|
||||
getPullRequestRepositoryAddress,
|
||||
getRepositoryRelays,
|
||||
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 { StatusIndicator } from "../StatusIndicator";
|
||||
import { useTimeline } from "@/hooks/useTimeline";
|
||||
import { useNostrEvent } from "@/hooks/useNostrEvent";
|
||||
import { AGGREGATOR_RELAYS } from "@/services/loaders";
|
||||
import { useRepositoryRelays } from "@/hooks/useRepositoryRelays";
|
||||
|
||||
/**
|
||||
* Renderer for Kind 1618 - Pull Request
|
||||
@@ -33,52 +29,8 @@ 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]);
|
||||
const { relays: statusRelays, repositoryEvent } =
|
||||
useRepositoryRelays(repoAddress);
|
||||
|
||||
// Fetch status events that reference this PR
|
||||
const statusFilter = useMemo(
|
||||
|
||||
53
src/hooks/useRepositoryRelays.ts
Normal file
53
src/hooks/useRepositoryRelays.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
import { useMemo } from "react";
|
||||
import { parseReplaceableAddress } from "applesauce-core/helpers/pointers";
|
||||
import { getOutboxes } from "applesauce-core/helpers";
|
||||
import { getRepositoryRelays } from "@/lib/nip34-helpers";
|
||||
import { useNostrEvent } from "@/hooks/useNostrEvent";
|
||||
import { AGGREGATOR_RELAYS } from "@/services/loaders";
|
||||
import type { NostrEvent } from "@/types/nostr";
|
||||
|
||||
/**
|
||||
* Resolves the relay list for a NIP-34 git repository address.
|
||||
*
|
||||
* Fallback chain:
|
||||
* 1. Relays from the repository event's `relays` tag (kind 30617)
|
||||
* 2. Repository owner's outbox relays (kind 10002)
|
||||
* 3. Well-known aggregator relays
|
||||
*
|
||||
* Also returns the repository event, needed by callers for getValidStatusAuthors.
|
||||
*/
|
||||
export function useRepositoryRelays(repoAddress: string | undefined): {
|
||||
relays: string[];
|
||||
repositoryEvent: NostrEvent | undefined;
|
||||
} {
|
||||
const repoPointer = useMemo(
|
||||
() => (repoAddress ? parseReplaceableAddress(repoAddress) : undefined),
|
||||
[repoAddress],
|
||||
);
|
||||
|
||||
const repositoryEvent = useNostrEvent(repoPointer);
|
||||
|
||||
const authorRelayListPointer = useMemo(
|
||||
() =>
|
||||
repoPointer
|
||||
? { kind: 10002, pubkey: repoPointer.pubkey, identifier: "" }
|
||||
: undefined,
|
||||
[repoPointer],
|
||||
);
|
||||
|
||||
const authorRelayList = useNostrEvent(authorRelayListPointer);
|
||||
|
||||
const relays = useMemo(() => {
|
||||
if (repositoryEvent) {
|
||||
const repoRelays = getRepositoryRelays(repositoryEvent);
|
||||
if (repoRelays.length > 0) return repoRelays;
|
||||
}
|
||||
if (authorRelayList) {
|
||||
const outbox = getOutboxes(authorRelayList);
|
||||
if (outbox.length > 0) return outbox;
|
||||
}
|
||||
return AGGREGATOR_RELAYS;
|
||||
}, [repositoryEvent, authorRelayList]);
|
||||
|
||||
return { relays, repositoryEvent };
|
||||
}
|
||||
Reference in New Issue
Block a user