preloading for issues, prs, and strings
10 unmodified lines11121314151516171819232 unmodified linesExpand all25225325425525625725825926026126226326426526626726826927027127227327427510 unmodified lines UserRound,} from 'lucide-solid';import { A } from '@solidjs/router';import { Match, Show, Switch, createEffect, createMemo, createSignal, type Component } from 'solid-js';import { createQuery } from '@tanstack/solid-query';import { createQuery, useQueryClient } from '@tanstack/solid-query';import { resolveAvatarUrl } from '../lib/api/identity';
export const ToggleButton: Component<{ active: boolean;232 unmodified linesExpand all 'untangled-input w-full rounded border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-900 px-3 py-2 text-sm text-gray-900 dark:text-gray-100';
export const textareaStyles = (): string => 'untangled-textarea w-full rounded border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-900 px-3 py-2 text-sm text-gray-900 dark:text-gray-100';
export const usePreloader = () => { const queryClient = useQueryClient();
return (queryKey: readonly unknown[], queryFn: () => Promise<any>, delayMs = 30) => { let hoverTimer: ReturnType<typeof setTimeout> | undefined;
const onMouseEnter = () => { hoverTimer = setTimeout(() => { queryClient.prefetchQuery({ queryKey, queryFn }); }, delayMs); };
const onMouseLeave = () => { clearTimeout(hoverTimer); };
return { onMouseEnter, onMouseLeave }; };};1377 unmodified linesExpand all13781379138013811382138313841385138613871388138913901391139213931394354 unmodified linesExpand all1377 unmodified linesExpand all createdAt: string; href: string; meta?: JSX.Element; children?: JSX.Element; onMouseEnter?: JSX.EventHandlerUnion<HTMLAnchorElement, MouseEvent>; onMouseLeave?: JSX.EventHandlerUnion<HTMLAnchorElement, MouseEvent>;}> = (props) => { return ( <A href={props.href} class="untangled-list-row" onMouseEnter={props.onMouseEnter} onMouseLeave={props.onMouseLeave} > <div class="min-w-0"> <div class="text-base text-gray-900 dark:text-gray-100"> {props.title}{' '}354 unmodified linesExpand all21 unmodified lines222324252626272829308 unmodified lines39404142434344454647923 unmodified linesExpand all971972973974975976977978979570 unmodified linesExpand all155015511552155315531554155515561557155815591555155615601561156215631564100 unmodified lines21 unmodified linesimport { A, useParams, useSearchParams, useNavigate } from '@solidjs/router';import { createQuery, useQueryClient } from '@tanstack/solid-query';import { For, Show, Switch, Match, createMemo, createSignal, createEffect, type Component } from 'solid-js';import type { Did } from '@atcute/lexicons/syntax';import { ErrorState, LoadingState, PlaceholderAvatar, SkeletonBlock, textareaStyles, inputStyles } from '../components/common';import { ErrorState, LoadingState, PlaceholderAvatar, SkeletonBlock, textareaStyles, inputStyles, usePreloader } from '../components/common';import { RepoCard, RepoCardSkeleton, StringCardSkeleton } from '../components/repo';import { listFollowRecords, createFollow,8 unmodified lines type VouchRecord,} from '../lib/api/graph';import { resolveActor, getActorProfile, resolveAvatarUrl, putActorProfile } from '../lib/api/identity';import { listRepoRecords, getRepoByDid, type RepoContext } from '../lib/api/repos';import { listStringRecords } from '../lib/api/strings';import { listStringRecords, getString } from '../lib/api/strings';import { useAuth } from '../lib/auth';import { listAllAppviewRecords } from '../lib/api/appview';import { formatRelativeTime, getErrorMessage } from '../lib/repo-utils';import { ShTangledFeedStar, ShTangledActorProfile } from '@atcute/tangled';923 unmodified linesExpand all const params = useParams(); const [searchParams, setSearchParams] = useSearchParams(); const queryClient = useQueryClient(); const auth = useAuth(); const preload = usePreloader(); const navigate = useNavigate(); const [repoSearch, setRepoSearch] = createSignal('');570 unmodified linesExpand all return ( <div class="border border-gray-200 dark:border-gray-700 rounded-sm"> <div class="py-4 px-6 rounded bg-white dark:bg-gray-800"> <div class="font-medium dark:text-white flex gap-2 items-center"> <A href={`/strings/${resolvedActor().handle || resolvedActor().did}/${item.rkey}`} class="hover:underline"> <A href={`/strings/${resolvedActor().handle || resolvedActor().did}/${item.rkey}`} class="hover:underline" {...preload(['string-detail', resolvedActor().handle || resolvedActor().did, item.rkey], () => getString(resolvedActor().handle || resolvedActor().did, item.rkey))} > {item.value.filename} </A> </div> </A> </div> {item.value.description && ( <div class="text-gray-600 dark:text-gray-300 text-sm"> {item.value.description} </div>100 unmodified lines15 unmodified lines161718192020212223222324252627282930156 unmodified linesExpand all187188189190191192193194195196197198199200201202203204205206207208209199210211212213214103 unmodified linesExpand all31831932032132232332432532632731 unmodified lines359360361362350363364365366367368369370371280 unmodified linesExpand all15 unmodified linesimport { createQuery } from '@tanstack/solid-query';import type { Did, ResourceUri } from '@atcute/lexicons/syntax';import { For, Match, Show, Switch, createEffect, createMemo, createSignal, type Component, type JSX } from 'solid-js';
import { ErrorState, LoadingState, PaginationControls } from '../components/common';import { ErrorState, LoadingState, PaginationControls, usePreloader } from '../components/common';import { RepoStatsList } from '../components/repo';import { getIssueRecord } from '../lib/api/issues';import { getPullRecord } from '../lib/api/pulls';import { getIssueRecord, getIssue } from '../lib/api/issues';import { getPullRecord, getPull } from '../lib/api/pulls';import { getRepoByDid } from '../lib/api/repos';import { getString } from '../lib/api/strings';import { issueQueryKey, pullQueryKey } from './repo/shared';import { ISSUE_COLLECTION,} from '../lib/api/constants';import {156 unmodified linesExpand all queryKey: ['repo-by-did', props.repoDid], queryFn: async () => getRepoByDid(props.repoDid), staleTime: 300_000, })); const preload = usePreloader(); const href = createMemo(() => { const repo = repoQuery.data; if (!repo) return undefined; const fn = props.kind === 'issue' ? issueHref : pullHref; const baseHref = fn(repo, props.threadRef); return props.commentRef ? `${baseHref}#comment-${props.commentRef}` : baseHref; }); const preloadProps = createMemo(() => { const repo = repoQuery.data; if (!repo) return {}; if (props.kind === 'issue') { return preload(issueQueryKey(props.repoDid, props.threadRef), () => getIssue(repo, props.threadRef)); } return preload(pullQueryKey(props.repoDid, props.threadRef), () => getPull(repo, props.threadRef)); });
return ( <Show when={href()} fallback={<span>{props.children}</span>}> {(target) => <A href={target()} class="truncate min-w-0">{props.children}</A>} {(target) => <A href={target()} class="truncate min-w-0" {...preloadProps()}>{props.children}</A>} </Show> );};
103 unmodified linesExpand all );};
const ResultTitle: Component<{ hit: SearchHit }> = (props) => { const preload = usePreloader();
const title = createMemo(() => { const value = props.hit.value as { title?: string; name?: string;31 unmodified lines {props.hit.author.handle} </A> </Match> <Match when={props.hit.nsid === 'sh.tangled.string'}> <A href={`/strings/${props.hit.author.handle || props.hit.author.did}/${props.hit.rkey}`} class="truncate min-w-0"> <A href={`/strings/${props.hit.author.handle || props.hit.author.did}/${props.hit.rkey}`} class="truncate min-w-0" {...preload(['string-detail', props.hit.author.handle || props.hit.author.did, props.hit.rkey], () => getString(props.hit.author.handle || props.hit.author.did, props.hit.rkey))} > {props.hit.author.handle || props.hit.author.did}/{title()} </A> </Match> <Match when={props.hit.nsid === 'sh.tangled.repo.issue.comment' || props.hit.nsid === 'sh.tangled.repo.pull.comment' || props.hit.nsid === 'sh.tangled.feed.comment'}>280 unmodified linesExpand all7 unmodified lines89101112121314151639 unmodified lines56575859606162636465101 unmodified linesExpand all16716816917016917117217317417517646 unmodified lines223224225226224227228229230231466 unmodified linesExpand all7 unmodified linesimport { resolveActor } from '../../lib/api/identity';import { parseAtUri } from '../../lib/api/records';import type { RepoContext } from '../../lib/api/repos';import { useAuth } from '../../lib/auth';import { Avatar, ErrorState, PaginationControls, StateBadge, ToggleButton, buttonStyles, cardStyles, inputStyles, textareaStyles } from '../../components/common';import { Avatar, ErrorState, PaginationControls, StateBadge, ToggleButton, buttonStyles, cardStyles, inputStyles, textareaStyles, usePreloader } from '../../components/common';import { AtUriPanel, MarkdownBlock, RepoFormSkeleton, RepoListPageSkeleton, RepoListSkeleton, RepoThreadSkeleton, IssueOrPullRow, ThreadCommentView } from '../../components/repo';import { RepoFrame, issueQueryKey, issuesQueryKey, useRepoQuery } from './shared';import { REPO_LIST_PAGE_LIMIT, formatRelativeTime, getErrorMessage, issueHref, parseIntegerSearchParam, uniqueCommenters, saveRecentIssueOrPull } from '../../lib/repo-utils';import { buildIssueCommentThreads, buildIssueFilterQuery, parseIssueFilter } from './issues-helpers';39 unmodified lines limit: String(pageLimit()), }); };
const preload = usePreloader();
const issuesQuery = createQuery(() => { const repo = repoQuery.data; return { queryKey: [...issuesQueryKey(repo?.repoDid ?? ''), stateFilter(), authorFilter(), textFilter(), pageOffset(), pageLimit()],101 unmodified linesExpand all state={issue.state} kind="issue" author={{ did: issue.author.did, handle: issue.author.handle }} createdAt={issue.value.createdAt} href={issueHref(repoQuery.data!, issue.uri)} href={issueHref(repoQuery.data!, issue.number || issue.rkey)} {...preload(issueQueryKey(repoQuery.data!.repoDid, String(issue.number || issue.rkey)), () => getIssue(repoQuery.data!, String(issue.number || issue.rkey)))} /> )} </For> </div>46 unmodified lines
try { const created = await createIssue(agent, repo, { title: title(), body: body() }); await client.invalidateQueries({ queryKey: issuesQueryKey(repo.repoDid) }); navigate(issueHref(repo, created.uri)); navigate(issueHref(repo, created.rkey)); } catch (cause) { setError(getErrorMessage(cause)); } finally { setSubmitting(false);466 unmodified linesExpand all25 unmodified lines2627282930303132333439 unmodified lines74757677787980818283107 unmodified linesExpand all191192193194193195196197198199200274 unmodified linesExpand all4754764774784764794804814824831155 unmodified linesExpand all25 unmodified linesimport { createPull, createPullComment, fetchPullRoundPatch, getPull, listPullsPage, mergePull, setPullStatus } from '../../lib/api/pulls';import { parseAtUri } from '../../lib/api/records';import { compareBranches, getRepoBranches, type RepoContext, compareFork, listRepoRecords } from '../../lib/api/repos';import { useAuth } from '../../lib/auth';import { Avatar, ErrorState, LoadingState, PaginationControls, StateBadge, ToggleButton, buttonStyles, cardStyles, textareaStyles } from '../../components/common';import { Avatar, ErrorState, LoadingState, PaginationControls, StateBadge, ToggleButton, buttonStyles, cardStyles, textareaStyles, usePreloader } from '../../components/common';import { AtUriPanel, BranchPill, DiffView, MarkdownBlock, PullComposeSkeleton, PullDiffSkeleton, PullDiffView, RepoListPageSkeleton, RepoListSkeleton, RepoThreadSkeleton, IssueOrPullRow, ThreadCommentView } from '../../components/repo';import { RepoFrame, pullQueryKey, pullsQueryKey, useRepoQuery } from './shared';import { REPO_LIST_PAGE_LIMIT, formatRelativeTime, getErrorMessage, parseIntegerSearchParam, pullHref, uniqueCommenters, saveRecentIssueOrPull } from '../../lib/repo-utils';import { buildPullFilterQuery, parsePastedPatchPrefill, parsePullFilter, type PullSourceMode, getRoundIndexForComment, buildPRCommentThreads } from './pulls-helpers';39 unmodified lines limit: String(pageLimit()), }); };
const preload = usePreloader();
const pullsQuery = createQuery(() => { const repo = repoQuery.data; return { queryKey: [...pullsQueryKey(repo?.repoDid ?? ''), stateFilter(), authorFilter(), textFilter(), pageOffset(), pageLimit()],107 unmodified linesExpand all state={pull.state} kind="pull" author={{ did: pull.author.did, handle: pull.author.handle }} createdAt={pull.value.createdAt} href={pullHref(repoQuery.data!, pull.uri)} href={pullHref(repoQuery.data!, pull.number || pull.rkey)} {...preload(pullQueryKey(repoQuery.data!.repoDid, String(pull.number || pull.rkey)), () => getPull(repoQuery.data!, String(pull.number || pull.rkey)))} meta={ <> <span>·</span> <span>{pull.value.rounds.length} round{pull.value.rounds.length === 1 ? '' : 's'}</span>274 unmodified linesExpand all patch, sourceRepoDid: mode === 'fork' ? (selectedForkDid() as Did) : undefined, }); await client.invalidateQueries({ queryKey: pullsQueryKey(repo.repoDid) }); navigate(pullHref(repo, created.uri)); navigate(pullHref(repo, created.rkey)); } catch (cause) { setError(getErrorMessage(cause)); } finally { setSubmitting(false);1155 unmodified linesExpand all