diff --git a/src/components/repo/RepoIssues.tsx b/src/components/repo/RepoIssues.tsx
index 6546eda..61a8ba9 100644
--- a/src/components/repo/RepoIssues.tsx
+++ b/src/components/repo/RepoIssues.tsx
@@ -1,13 +1,12 @@
import { parseResourceUri, type Did, type Handle } from '@atcute/lexicons'
+import { isDid } from '@atcute/lexicons/syntax'
import { IconCircleDot, IconMessageCircle } from '@tabler/icons-react'
import { Link } from 'react-router-dom'
-import { useRecordAuthors } from '../../hooks/useRecordAuthors'
+import { useVisibleActor } from '../../hooks/useVisibleActor'
import { useRepoIssues } from '../../hooks/useRepoIssues'
import { getRecordRkey, type Issue } from '../../lib/tangled/repo'
import { SurfaceCard } from '../shared/SurfaceCard'
-const EMPTY_ISSUES: readonly Issue[] = []
-
type RepoIssuesProps = {
repoOwnerHandle: Handle
repoDid: Did
@@ -16,7 +15,6 @@ type RepoIssuesProps = {
export function RepoIssues({ repoOwnerHandle, repoDid, repoKey }: RepoIssuesProps) {
const { issues, error } = useRepoIssues(repoDid)
- const { authors, error: authorsError } = useRecordAuthors(issues?.items ?? EMPTY_ISSUES)
if (error) {
return
Could not load issues: {error.message}
@@ -30,40 +28,43 @@ export function RepoIssues({ repoOwnerHandle, repoDid, repoKey }: RepoIssuesProp
return No issues found.
}
- const authorResolutionMessage =
- authorsError === null ? null : 'Some issues may not be available until their authors resolve.'
-
return (
- <>
- {authorResolutionMessage && {authorResolutionMessage}
}
-
- {issues.items.map((issue) => {
- const isOpen = issue.state === 'open'
- const authorDid = parseResourceUri(issue.uri).repo
- const issueOwner = authors?.get(authorDid)
- const issueUrl =
- issueOwner === undefined
- ? undefined
- : `/${repoOwnerHandle}/${repoKey}/issues/${issueOwner}/${getRecordRkey(issue.uri)}`
+
+ {issues.items.map((issue) => (
+
+ ))}
+
+ )
+}
- return (
-
- {issueUrl === undefined ? (
-
- ) : (
-
-
-
- )}
-
- )
- })}
-
- >
+type IssueListItemProps = { issue: Issue; repoKey: string; repoOwnerHandle: Handle }
+
+function IssueListItem({ issue, repoKey, repoOwnerHandle }: IssueListItemProps) {
+ const authorIdentifier = parseResourceUri(issue.uri).repo
+ const { actor, elementRef } = useVisibleActor(isDid(authorIdentifier) ? authorIdentifier : null)
+ const issueUrl = actor
+ ? `/${repoOwnerHandle}/${repoKey}/issues/${actor.miniDoc.handle}/${getRecordRkey(issue.uri)}`
+ : undefined
+
+ return (
+
+ {issueUrl === undefined ? (
+
+ ) : (
+
+
+
+ )}
+
)
}
diff --git a/src/components/repo/RepoPulls.tsx b/src/components/repo/RepoPulls.tsx
index f84cadc..202e855 100644
--- a/src/components/repo/RepoPulls.tsx
+++ b/src/components/repo/RepoPulls.tsx
@@ -1,13 +1,12 @@
import { parseResourceUri, type Did, type Handle } from '@atcute/lexicons'
+import { isDid } from '@atcute/lexicons/syntax'
import { IconGitPullRequest, IconMessageCircle } from '@tabler/icons-react'
import { Link } from 'react-router-dom'
-import { useRecordAuthors } from '../../hooks/useRecordAuthors'
+import { useVisibleActor } from '../../hooks/useVisibleActor'
import { useRepoPulls } from '../../hooks/useRepoPulls'
import { getRecordRkey, type Pull } from '../../lib/tangled/repo'
import { SurfaceCard } from '../shared/SurfaceCard'
-const EMPTY_PULLS: readonly Pull[] = []
-
type RepoPullsProps = {
repoOwnerHandle: Handle
repoDid: Did
@@ -16,7 +15,6 @@ type RepoPullsProps = {
export function RepoPulls({ repoOwnerHandle, repoDid, repoKey }: RepoPullsProps) {
const { pulls, error } = useRepoPulls(repoDid)
- const { authors, error: authorsError } = useRecordAuthors(pulls?.items ?? EMPTY_PULLS)
if (error) {
return Could not load pulls: {error.message}
@@ -30,47 +28,49 @@ export function RepoPulls({ repoOwnerHandle, repoDid, repoKey }: RepoPullsProps)
return No pulls found.
}
- const authorResolutionMessage =
- authorsError === null
- ? null
- : 'Some pull requests may not be available until their authors resolve.'
-
return (
- <>
- {authorResolutionMessage && {authorResolutionMessage}
}
-
- {pulls.items.map((pull) => {
- const stateClassName =
- pull.state === 'open'
- ? 'text-ctp-teal'
- : pull.state === 'merged'
- ? 'text-ctp-mauve'
- : 'text-ctp-overlay-1'
- const authorDid = parseResourceUri(pull.uri).repo
- const pullOwner = authors?.get(authorDid)
- const pullUrl =
- pullOwner === undefined
- ? undefined
- : `/${repoOwnerHandle}/${repoKey}/pulls/${pullOwner}/${getRecordRkey(pull.uri)}`
+
+ {pulls.items.map((pull) => (
+
+ ))}
+
+ )
+}
- return (
-
- {pullUrl === undefined ? (
-
- ) : (
-
-
-
- )}
-
- )
- })}
-
- >
+type PullListItemProps = { pull: Pull; repoKey: string; repoOwnerHandle: Handle }
+
+function PullListItem({ pull, repoKey, repoOwnerHandle }: PullListItemProps) {
+ const authorIdentifier = parseResourceUri(pull.uri).repo
+ const { actor, elementRef } = useVisibleActor(isDid(authorIdentifier) ? authorIdentifier : null)
+ const pullUrl = actor
+ ? `/${repoOwnerHandle}/${repoKey}/pulls/${actor.miniDoc.handle}/${getRecordRkey(pull.uri)}`
+ : undefined
+ const stateClassName =
+ pull.state === 'open'
+ ? 'text-ctp-teal'
+ : pull.state === 'merged'
+ ? 'text-ctp-mauve'
+ : 'text-ctp-overlay-1'
+
+ return (
+
+ {pullUrl === undefined ? (
+
+ ) : (
+
+
+
+ )}
+
)
}
diff --git a/src/components/vouch/VouchList.tsx b/src/components/vouch/VouchList.tsx
index 4783239..33854d6 100644
--- a/src/components/vouch/VouchList.tsx
+++ b/src/components/vouch/VouchList.tsx
@@ -1,5 +1,4 @@
import type { VouchRecord } from '../../lib/tangled/graph'
-import { useResolvedVouches } from '../../hooks/useResolvedVouches'
import { VouchListItem } from './VouchListItem'
type VouchListProps = {
@@ -7,20 +6,10 @@ type VouchListProps = {
}
export function VouchList({ vouches }: VouchListProps) {
- const { resolvedVouches, isLoading } = useResolvedVouches(vouches)
-
- if (isLoading) {
- return Loading vouches...
- }
-
- if (resolvedVouches.length === 0) {
- return Vouches could not be resolved.
- }
-
return (
- {resolvedVouches.map(({ vouch, author }) => (
-
+ {vouches.map((vouch) => (
+
))}
)
diff --git a/src/components/vouch/VouchListItem.tsx b/src/components/vouch/VouchListItem.tsx
index 4d845d8..ebb81f0 100644
--- a/src/components/vouch/VouchListItem.tsx
+++ b/src/components/vouch/VouchListItem.tsx
@@ -1,17 +1,20 @@
import { IconThumbDown, IconThumbUp } from '@tabler/icons-react'
+import { parseResourceUri } from '@atcute/lexicons'
+import { isDid } from '@atcute/lexicons/syntax'
import { Link } from 'react-router-dom'
-import type { ResolvedActor } from '../../lib/actor'
+import { useVisibleActor } from '../../hooks/useVisibleActor'
import { ProfileAvatar } from '../profile/ProfileAvatar'
import { SurfaceCard } from '../shared/SurfaceCard'
import type { VouchRecord } from '../../lib/tangled/graph'
type VouchListItemProps = {
vouchRecord: VouchRecord
- author: ResolvedActor
}
-export function VouchListItem({ vouchRecord, author }: VouchListItemProps) {
+export function VouchListItem({ vouchRecord }: VouchListItemProps) {
const { value } = vouchRecord
+ const authorIdentifier = parseResourceUri(vouchRecord.uri).repo
+ const { actor, elementRef } = useVisibleActor(isDid(authorIdentifier) ? authorIdentifier : null)
const isDenouncement = value.kind === 'denounce'
const actionLabel = isDenouncement ? 'Denounces' : 'Vouches'
const actionColor = isDenouncement ? 'text-ctp-red' : 'text-ctp-green'
@@ -19,43 +22,55 @@ export function VouchListItem({ vouchRecord, author }: VouchListItemProps) {
const ActionIcon = isDenouncement ? IconThumbDown : IconThumbUp
return (
-
-
-
-
+
+
+
+ {actor ? (
+
+
-
- {author.miniDoc.handle}
-
-
+
+ {actor.miniDoc.handle}
+
+
+ ) : (
+
+
+ {authorIdentifier}
+
+ )}
-
-
- {actionLabel}
-
+
+
+ {actionLabel}
+
-
- {new Date(value.createdAt).toLocaleDateString()}
-
-
+
+ {new Date(value.createdAt).toLocaleDateString()}
+
+
- {value.reason && (
-
- {value.reason}
-
- )}
-
+ {value.reason && (
+
+ {value.reason}
+
+ )}
+
+
)
}
diff --git a/src/hooks/useRecordAuthors.ts b/src/hooks/useRecordAuthors.ts
deleted file mode 100644
index 7732c94..0000000
--- a/src/hooks/useRecordAuthors.ts
+++ /dev/null
@@ -1,57 +0,0 @@
-import { parseResourceUri, type Handle, type ResourceUri } from '@atcute/lexicons'
-import { useEffect, useMemo, useState } from 'react'
-import { resolveMiniDoc } from '../lib/actor'
-
-type RecordWithUri = {
- uri: ResourceUri
-}
-
-type AuthorsState = {
- authors: Map
- error: Error | null
- key: string
-}
-
-export function useRecordAuthors(records: readonly RecordWithUri[]) {
- const authorDids = useMemo(
- () => [...new Set(records.map(({ uri }) => parseResourceUri(uri).repo))],
- [records],
- )
- const authorsKey = [...authorDids].sort().join(',')
- const [state, setState] = useState(null)
-
- useEffect(() => {
- let isCancelled = false
-
- void Promise.allSettled(
- authorDids.map(async (did) => [did, (await resolveMiniDoc(did)).handle] as const),
- ).then((results) => {
- if (isCancelled) return
-
- const authors = new Map()
- const failedResolutions = results.filter((result) => result.status === 'rejected')
- for (const result of results) {
- if (result.status === 'fulfilled') authors.set(...result.value)
- }
-
- setState({
- authors,
- error:
- failedResolutions.length > 0
- ? new Error('Some record authors could not be resolved')
- : null,
- key: authorsKey,
- })
- })
-
- return () => {
- isCancelled = true
- }
- }, [authorDids, authorsKey])
-
- const isCurrentList = state?.key === authorsKey
- return {
- authors: isCurrentList ? (state.authors ?? null) : null,
- error: isCurrentList ? (state.error ?? null) : null,
- }
-}
diff --git a/src/hooks/useResolvedVouches.ts b/src/hooks/useResolvedVouches.ts
deleted file mode 100644
index c0a1e87..0000000
--- a/src/hooks/useResolvedVouches.ts
+++ /dev/null
@@ -1,55 +0,0 @@
-import { parseResourceUri, type Did } from '@atcute/lexicons'
-import { isDid } from '@atcute/lexicons/syntax'
-import { useEffect, useState } from 'react'
-import { resolveActor, type ResolvedActor } from '../lib/actor'
-import type { VouchRecord } from '../lib/tangled/graph'
-
-export type ResolvedVouch = { author: ResolvedActor; vouch: VouchRecord }
-
-export function useResolvedVouches(vouches: VouchRecord[]) {
- const [resolvedVouches, setResolvedVouches] = useState([])
- const [isLoading, setIsLoading] = useState(true)
-
- useEffect(() => {
- let isCancelled = false
-
- async function loadVouches() {
- setIsLoading(true)
- const authorDids = [
- ...new Set(vouches.map(getVouchAuthorDid).filter((did): did is Did => did !== null)),
- ]
- const results = await Promise.allSettled(
- authorDids.map(async (did): Promise<[Did, ResolvedActor]> => [
- did,
- await resolveActor(did),
- ]),
- )
-
- if (isCancelled) return
-
- const authors = new Map(
- results.flatMap((result) => (result.status === 'fulfilled' ? [result.value] : [])),
- )
- setResolvedVouches(
- vouches.flatMap((vouch) => {
- const authorDid = getVouchAuthorDid(vouch)
- const author = authorDid === null ? undefined : authors.get(authorDid)
- return author === undefined ? [] : [{ vouch, author }]
- }),
- )
- setIsLoading(false)
- }
-
- void loadVouches()
- return () => {
- isCancelled = true
- }
- }, [vouches])
-
- return { resolvedVouches, isLoading }
-}
-
-function getVouchAuthorDid(vouch: VouchRecord): Did | null {
- const { repo } = parseResourceUri(vouch.uri)
- return isDid(repo) ? repo : null
-}