mirror of
https://github.com/sourcegraph/sourcegraph.git
synced 2026-02-06 18:51:59 +00:00
add 'changelists' page
This commit is contained in:
parent
f3257cb357
commit
51685f116e
25
client/web-sveltekit/src/lib/Changelist.gql
Normal file
25
client/web-sveltekit/src/lib/Changelist.gql
Normal file
@ -0,0 +1,25 @@
|
||||
fragment Changelist on GitCommit {
|
||||
id
|
||||
subject
|
||||
body
|
||||
author {
|
||||
date
|
||||
person {
|
||||
name
|
||||
email
|
||||
...Avatar_Person
|
||||
}
|
||||
}
|
||||
committer {
|
||||
date
|
||||
person {
|
||||
name
|
||||
email
|
||||
...Avatar_Person
|
||||
}
|
||||
}
|
||||
perforceChangelist {
|
||||
canonicalURL
|
||||
cid
|
||||
}
|
||||
}
|
||||
156
client/web-sveltekit/src/lib/Changelist.svelte
Normal file
156
client/web-sveltekit/src/lib/Changelist.svelte
Normal file
@ -0,0 +1,156 @@
|
||||
<svelte:options immutable />
|
||||
|
||||
<script lang="ts">
|
||||
import Avatar from '$lib/Avatar.svelte'
|
||||
import Icon from '$lib/Icon.svelte'
|
||||
import Timestamp from '$lib/Timestamp.svelte'
|
||||
import Tooltip from '$lib/Tooltip.svelte'
|
||||
|
||||
import type { Changelist } from './Changelist.gql'
|
||||
import { isViewportMobile } from './stores'
|
||||
import Button from './wildcard/Button.svelte'
|
||||
|
||||
export let changelist: Changelist
|
||||
export let alwaysExpanded: boolean = false
|
||||
|
||||
$: expanded = alwaysExpanded
|
||||
|
||||
$: author = changelist.author
|
||||
$: commitDate = new Date(author.date)
|
||||
$: authorAvatarTooltip = author.person.name + (author ? ' (author)' : '')
|
||||
</script>
|
||||
|
||||
<div class="root">
|
||||
<div class="avatar">
|
||||
<Tooltip tooltip={authorAvatarTooltip}>
|
||||
<Avatar avatar={author.person} />
|
||||
</Tooltip>
|
||||
</div>
|
||||
<div class="title">
|
||||
<a class="subject" href={changelist.perforceChangelist?.canonicalURL}>{changelist.subject}</a>
|
||||
{#if !alwaysExpanded && changelist.body && !$isViewportMobile}
|
||||
<Button
|
||||
variant="secondary"
|
||||
size="sm"
|
||||
on:click={() => (expanded = !expanded)}
|
||||
aria-label="{expanded ? 'Hide' : 'Show'} changelist message"
|
||||
>
|
||||
<Icon icon={ILucideEllipsis} inline aria-hidden />
|
||||
</Button>
|
||||
{/if}
|
||||
</div>
|
||||
<div class="author">
|
||||
submitted by <strong>{author.person.name}</strong>
|
||||
<Timestamp date={commitDate} />
|
||||
</div>
|
||||
{#if changelist.body}
|
||||
<div class="message" class:expanded>
|
||||
{#if $isViewportMobile}
|
||||
{#if expanded}
|
||||
<Button variant="secondary" size="lg" display="block" on:click={() => (expanded = false)}>
|
||||
Close
|
||||
</Button>
|
||||
{:else}
|
||||
<Button variant="secondary" size="sm" display="block" on:click={() => (expanded = true)}>
|
||||
Show changelist message
|
||||
</Button>
|
||||
{/if}
|
||||
{/if}
|
||||
|
||||
<pre>{changelist.body}</pre>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.root {
|
||||
display: grid;
|
||||
overflow: hidden;
|
||||
grid-template-columns: auto 1fr;
|
||||
grid-template-areas: 'avatar title' 'avatar author' '. message';
|
||||
column-gap: 1rem;
|
||||
|
||||
@media (--mobile) {
|
||||
grid-template-columns: auto 1fr;
|
||||
grid-template-areas: 'avatar title' 'author author' 'message message';
|
||||
row-gap: 0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.avatar {
|
||||
grid-area: avatar;
|
||||
display: flex;
|
||||
gap: 0.25rem;
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
.title {
|
||||
grid-area: title;
|
||||
align-self: center;
|
||||
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
align-items: center;
|
||||
overflow: hidden;
|
||||
|
||||
.subject {
|
||||
font-weight: 600;
|
||||
flex: 0 1 auto;
|
||||
color: var(--body-color);
|
||||
min-width: 0;
|
||||
|
||||
@media (--sm-breakpoint-up) {
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.author {
|
||||
grid-area: author;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.message {
|
||||
grid-area: message;
|
||||
overflow: hidden;
|
||||
|
||||
@media (--mobile) {
|
||||
&.expanded {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
margin: 0;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
background-color: var(--color-bg-1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pre {
|
||||
display: none;
|
||||
margin-top: 0.5rem;
|
||||
margin-bottom: 1.5rem;
|
||||
|
||||
font-size: 0.75rem;
|
||||
max-width: 100%;
|
||||
word-wrap: break-word;
|
||||
white-space: pre-wrap;
|
||||
|
||||
.expanded & {
|
||||
display: block;
|
||||
}
|
||||
|
||||
@media (--mobile) {
|
||||
padding: 0.5rem;
|
||||
overflow: auto;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -17,7 +17,7 @@
|
||||
|
||||
export let name: string
|
||||
export let seeAllItemsURL: string
|
||||
export let getData: (query: string) => PromiseLike<Result<T>>
|
||||
export let getData: ((query: string) => PromiseLike<Result<T>>) | (() => PromiseLike<Result<T>>)
|
||||
export let onSelect: (item: T) => void
|
||||
export let toOption: (item: T) => ComboboxOptionProps<string>
|
||||
|
||||
|
||||
@ -13,6 +13,9 @@
|
||||
export type RepositoryCommits = { nodes: RevPickerGitCommit[] }
|
||||
export type RepositoryGitCommit = RevPickerGitCommit
|
||||
|
||||
export type DepotChangelists = { nodes: RevPickerChangelist[] }
|
||||
export type DepotChangelist = RevPickerChangelist
|
||||
|
||||
const branchesHotkey: Keys = {
|
||||
key: 'shift+b',
|
||||
}
|
||||
@ -28,6 +31,7 @@
|
||||
|
||||
<script lang="ts">
|
||||
import type { Placement } from '@floating-ui/dom'
|
||||
import type { ComponentProps } from 'svelte'
|
||||
import type { HTMLButtonAttributes } from 'svelte/elements'
|
||||
|
||||
import { goto } from '$app/navigation'
|
||||
@ -42,9 +46,10 @@
|
||||
import ButtonGroup from '$lib/wildcard/ButtonGroup.svelte'
|
||||
import CopyButton from '$lib/wildcard/CopyButton.svelte'
|
||||
|
||||
import { RevPickerChangelist } from '../../routes/[...repo=reporev]/layout.gql'
|
||||
|
||||
import Picker from './Picker.svelte'
|
||||
import RepositoryRevPickerItem from './RepositoryRevPickerItem.svelte'
|
||||
import type { ComponentProps } from 'svelte'
|
||||
|
||||
type $$Props = HTMLButtonAttributes & {
|
||||
repoURL: string
|
||||
@ -57,6 +62,7 @@
|
||||
getRepositoryTags: (query: string) => PromiseLike<RepositoryTags>
|
||||
getRepositoryCommits: (query: string) => PromiseLike<RepositoryCommits>
|
||||
getRepositoryBranches: (query: string) => PromiseLike<RepositoryBranches>
|
||||
getDepotChangelists: (query: string) => PromiseLike<DepotChangelists>
|
||||
}
|
||||
|
||||
export let repoURL: $$Props['repoURL']
|
||||
@ -75,6 +81,7 @@
|
||||
export let getRepositoryTags: (query: string) => PromiseLike<RepositoryTags>
|
||||
export let getRepositoryCommits: (query: string) => PromiseLike<RepositoryCommits>
|
||||
export let getRepositoryBranches: (query: string) => PromiseLike<RepositoryBranches>
|
||||
export let getDepotChangelists: (query: string) => PromiseLike<DepotChangelists>
|
||||
|
||||
function defaultHandleSelect(revision: string) {
|
||||
goto(replaceRevisionInURL(location.pathname + location.search + location.hash, revision))
|
||||
@ -86,6 +93,7 @@
|
||||
$: isOnSpecificRev = revisionLabel !== defaultBranch
|
||||
|
||||
const buttonClass = getButtonClassName({ variant: 'secondary', outline: false, size: 'sm' })
|
||||
$: isPerforceDepot = getDepotChangelists !== null
|
||||
</script>
|
||||
|
||||
<Popover let:registerTrigger let:registerTarget let:toggle {placement}>
|
||||
@ -121,73 +129,97 @@
|
||||
|
||||
<div slot="content" class="content" let:toggle>
|
||||
<Tabs>
|
||||
<TabPanel title="Branches" shortcut={branchesHotkey}>
|
||||
<Picker
|
||||
name="branches"
|
||||
seeAllItemsURL={`${repoURL}/-/branches`}
|
||||
getData={getRepositoryBranches}
|
||||
toOption={branch => ({ value: branch.id, label: branch.displayName })}
|
||||
onSelect={branch => {
|
||||
toggle(false)
|
||||
onSelect(branch.abbrevName)
|
||||
}}
|
||||
let:value
|
||||
>
|
||||
<RepositoryRevPickerItem
|
||||
icon={ILucideGitBranch}
|
||||
label={value.displayName}
|
||||
author={value.target.commit?.author}
|
||||
{#if !isPerforceDepot}
|
||||
<TabPanel title="Branches" shortcut={branchesHotkey}>
|
||||
<Picker
|
||||
name="branches"
|
||||
seeAllItemsURL={`${repoURL}/-/branches`}
|
||||
getData={getRepositoryBranches}
|
||||
toOption={branch => ({ value: branch.id, label: branch.displayName })}
|
||||
onSelect={branch => {
|
||||
toggle(false)
|
||||
onSelect(branch.abbrevName)
|
||||
}}
|
||||
let:value
|
||||
>
|
||||
<svelte:fragment slot="title">
|
||||
<Icon icon={ILucideGitBranch} inline aria-hidden="true" />
|
||||
<Badge variant="link">{value.displayName}</Badge>
|
||||
{#if value.displayName === defaultBranch}
|
||||
<Badge variant="secondary" small>DEFAULT</Badge>
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
</RepositoryRevPickerItem>
|
||||
</Picker>
|
||||
</TabPanel>
|
||||
<TabPanel title="Tags" shortcut={tagsHotkey}>
|
||||
<Picker
|
||||
name="tags"
|
||||
seeAllItemsURL={`${repoURL}/-/tags`}
|
||||
getData={getRepositoryTags}
|
||||
toOption={tag => ({ value: tag.id, label: tag.displayName })}
|
||||
onSelect={tag => {
|
||||
toggle(false)
|
||||
onSelect(tag.abbrevName)
|
||||
}}
|
||||
let:value
|
||||
>
|
||||
<RepositoryRevPickerItem
|
||||
icon={ILucideTag}
|
||||
label={value.displayName}
|
||||
author={value.target.commit?.author}
|
||||
/>
|
||||
</Picker>
|
||||
</TabPanel>
|
||||
<TabPanel title="Commits" shortcut={commitsHotkey}>
|
||||
<Picker
|
||||
name="commits"
|
||||
seeAllItemsURL={`${repoURL}/-/commits`}
|
||||
getData={getRepositoryCommits}
|
||||
toOption={commit => ({ value: commit.id, label: commit.oid })}
|
||||
onSelect={commit => {
|
||||
toggle(false)
|
||||
onSelect(commit.oid)
|
||||
}}
|
||||
let:value
|
||||
>
|
||||
<RepositoryRevPickerItem label="" author={value.author}>
|
||||
<svelte:fragment slot="title">
|
||||
<Icon icon={ILucideGitCommitVertical} inline aria-hidden="true" />
|
||||
<Badge variant="link">{value.abbreviatedOID}</Badge>
|
||||
<span class="commit-subject">{value.subject}</span>
|
||||
</svelte:fragment>
|
||||
</RepositoryRevPickerItem>
|
||||
</Picker>
|
||||
</TabPanel>
|
||||
<RepositoryRevPickerItem
|
||||
icon={ILucideGitBranch}
|
||||
label={value.displayName}
|
||||
author={value.target.commit?.author}
|
||||
>
|
||||
<svelte:fragment slot="title">
|
||||
<Icon icon={ILucideGitBranch} inline aria-hidden="true" />
|
||||
<Badge variant="link">{value.displayName}</Badge>
|
||||
{#if value.displayName === defaultBranch}
|
||||
<Badge variant="secondary" small>DEFAULT</Badge>
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
</RepositoryRevPickerItem>
|
||||
</Picker>
|
||||
</TabPanel>
|
||||
<TabPanel title="Tags" shortcut={tagsHotkey}>
|
||||
<Picker
|
||||
name="tags"
|
||||
seeAllItemsURL={`${repoURL}/-/tags`}
|
||||
getData={getRepositoryTags}
|
||||
toOption={tag => ({ value: tag.id, label: tag.displayName })}
|
||||
onSelect={tag => {
|
||||
toggle(false)
|
||||
onSelect(tag.abbrevName)
|
||||
}}
|
||||
let:value
|
||||
>
|
||||
<RepositoryRevPickerItem
|
||||
icon={ILucideTag}
|
||||
label={value.displayName}
|
||||
author={value.target.commit?.author}
|
||||
/>
|
||||
</Picker>
|
||||
</TabPanel>
|
||||
<TabPanel title="Commits" shortcut={commitsHotkey}>
|
||||
<Picker
|
||||
name="commits"
|
||||
seeAllItemsURL={`${repoURL}/-/commits`}
|
||||
getData={getRepositoryCommits}
|
||||
toOption={commit => ({ value: commit.id, label: commit.oid })}
|
||||
onSelect={commit => {
|
||||
toggle(false)
|
||||
onSelect(commit.oid)
|
||||
}}
|
||||
let:value
|
||||
>
|
||||
<RepositoryRevPickerItem label="" author={value.author}>
|
||||
<svelte:fragment slot="title">
|
||||
<Icon icon={ILucideGitCommitVertical} inline aria-hidden="true" />
|
||||
<Badge variant="link">{value.abbreviatedOID}</Badge>
|
||||
<span class="commit-subject">{value.subject}</span>
|
||||
</svelte:fragment>
|
||||
</RepositoryRevPickerItem>
|
||||
</Picker>
|
||||
</TabPanel>
|
||||
{:else}
|
||||
<TabPanel title="Changelists" shortcut={commitsHotkey}>
|
||||
<Picker
|
||||
name="changelists"
|
||||
seeAllItemsURL={`${repoURL}/-/changelists`}
|
||||
getData={getDepotChangelists}
|
||||
toOption={changelist => ({ value: changelist.id, label: changelist.perforceChangelist?.cid })}
|
||||
onSelect={changelist => {
|
||||
toggle(false)
|
||||
onSelect(changelist.perforceChangelist?.cid ?? '')
|
||||
}}
|
||||
let:value
|
||||
>
|
||||
<RepositoryRevPickerItem label="" author={value.author}>
|
||||
<svelte:fragment slot="title">
|
||||
<Icon icon={ILucideGitCommitVertical} inline aria-hidden="true" />
|
||||
<Badge variant="link">{value.perforceChangelist?.cid}</Badge>
|
||||
<span class="changelist-subject">{value.subject}</span>
|
||||
</svelte:fragment>
|
||||
</RepositoryRevPickerItem>
|
||||
</Picker>
|
||||
</TabPanel>
|
||||
{/if}
|
||||
</Tabs>
|
||||
</div>
|
||||
</Popover>
|
||||
@ -258,7 +290,8 @@
|
||||
}
|
||||
}
|
||||
|
||||
.commit-subject {
|
||||
.commit-subject,
|
||||
.changelist-subject {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
@ -0,0 +1,215 @@
|
||||
<script lang="ts">
|
||||
// @sg EnableRollout
|
||||
import { get } from 'svelte/store'
|
||||
|
||||
import { navigating } from '$app/stores'
|
||||
import Changelist from '$lib/Changelist.svelte'
|
||||
import LoadingSpinner from '$lib/LoadingSpinner.svelte'
|
||||
import RepositoryRevPicker from '$lib/repo/RepositoryRevPicker.svelte'
|
||||
import Scroller, { type Capture as ScrollerCapture } from '$lib/Scroller.svelte'
|
||||
import { Alert, Badge } from '$lib/wildcard'
|
||||
|
||||
import type { PageData, Snapshot } from './$types'
|
||||
|
||||
export let data: PageData
|
||||
|
||||
// This tracks the number of changelists that have been loaded and the current scroll
|
||||
// position, so both can be restored when the user refreshes the page or navigates
|
||||
// back to it.
|
||||
export const snapshot: Snapshot<{
|
||||
changelists: ReturnType<typeof data.changelistsQuery.capture>
|
||||
scroller: ScrollerCapture
|
||||
}> = {
|
||||
capture() {
|
||||
return {
|
||||
changelists: changelistsQuery.capture(),
|
||||
scroller: scroller.capture(),
|
||||
}
|
||||
},
|
||||
async restore(snapshot) {
|
||||
if (get(navigating)?.type === 'popstate') {
|
||||
await changelistsQuery?.restore(snapshot.changelists)
|
||||
}
|
||||
scroller.restore(snapshot.scroller)
|
||||
},
|
||||
}
|
||||
|
||||
function fetchMore() {
|
||||
changelistsQuery?.fetchMore()
|
||||
}
|
||||
|
||||
let scroller: Scroller
|
||||
|
||||
$: changelistsQuery = data.changelistsQuery
|
||||
$: changelists = $changelistsQuery.data
|
||||
$: pageTitle = (() => {
|
||||
const parts = ['Commits']
|
||||
if (data.path) {
|
||||
parts.push(data.path)
|
||||
}
|
||||
parts.push(data.displayRepoName, 'Sourcegraph')
|
||||
return parts.join(' - ')
|
||||
})()
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>{pageTitle}</title>
|
||||
</svelte:head>
|
||||
|
||||
<header>
|
||||
<h2>
|
||||
Changelists
|
||||
{#if data.path}
|
||||
in <code>{data.path}</code>
|
||||
{/if}
|
||||
</h2>
|
||||
<div>
|
||||
<RepositoryRevPicker
|
||||
repoURL={data.repoURL}
|
||||
revision={data.revision}
|
||||
commitID={data.resolvedRevision.commitID}
|
||||
defaultBranch={data.defaultBranch}
|
||||
placement="bottom-start"
|
||||
getRepositoryBranches={data.getRepoBranches}
|
||||
getRepositoryCommits={data.getRepoCommits}
|
||||
getRepositoryTags={data.getRepoTags}
|
||||
getDepotChangelists={data.getDepotChangelists}
|
||||
/>
|
||||
</div>
|
||||
</header>
|
||||
<section>
|
||||
<Scroller bind:this={scroller} margin={600} on:more={fetchMore}>
|
||||
{#if changelists}
|
||||
<ul class="changelists">
|
||||
{#each changelists as changelist (changelist.perforceChangelist?.canonicalURL)}
|
||||
<li>
|
||||
<div class="changelist">
|
||||
<Changelist {changelist} />
|
||||
</div>
|
||||
<ul class="actions">
|
||||
<li>
|
||||
Changelist ID:
|
||||
<Badge variant="link">
|
||||
<a href={changelist.perforceChangelist?.canonicalURL} title="View changelist"
|
||||
>{changelist.perforceChangelist?.cid}</a
|
||||
>
|
||||
</Badge>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/{data.repoName}@changelist/{changelist.perforceChangelist?.cid}"
|
||||
>Browse files</a
|
||||
></li
|
||||
>
|
||||
</ul>
|
||||
</li>
|
||||
{:else}
|
||||
<li>
|
||||
<Alert variant="info">No changelists found</Alert>
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
{/if}
|
||||
{#if $changelistsQuery.fetching}
|
||||
<div class="footer">
|
||||
<LoadingSpinner />
|
||||
</div>
|
||||
{:else if !$changelistsQuery.fetching && $changelistsQuery.error}
|
||||
<div class="footer">
|
||||
<Alert variant="danger">
|
||||
Unable to fetch changelists: {$changelistsQuery.error.message}
|
||||
</Alert>
|
||||
</div>
|
||||
{/if}
|
||||
</Scroller>
|
||||
</section>
|
||||
|
||||
<style lang="scss">
|
||||
section {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
ul {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
h2 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
header {
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
div {
|
||||
width: min-content;
|
||||
}
|
||||
}
|
||||
|
||||
header,
|
||||
ul.changelists,
|
||||
.footer {
|
||||
max-width: var(--viewport-xl);
|
||||
width: 100%;
|
||||
margin: 0 auto;
|
||||
padding: 1rem;
|
||||
|
||||
@media (--mobile) {
|
||||
padding: 0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
ul {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
ul.changelists {
|
||||
--avatar-size: 2.5rem;
|
||||
padding-top: 0;
|
||||
|
||||
> li {
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
display: flex;
|
||||
padding: 0.5rem 0;
|
||||
gap: 1rem;
|
||||
|
||||
@media (--mobile) {
|
||||
display: block;
|
||||
|
||||
.actions {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
margin-top: 0.5rem;
|
||||
|
||||
li:not(:last-child)::after {
|
||||
content: '•';
|
||||
padding-left: 0.5rem;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.changelist {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.actions {
|
||||
--icon-color: currentColor;
|
||||
text-align: right;
|
||||
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.footer {
|
||||
&:not(:first-child) {
|
||||
border-top: 1px solid var(--border-color);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,50 @@
|
||||
import { IncrementalRestoreStrategy, getGraphQLClient, infinityQuery } from "$lib/graphql"
|
||||
import { resolveRevision } from "$lib/repo/utils"
|
||||
import { parseRepoRevision } from "@sourcegraph/shared/src/util/url"
|
||||
import type { PageLoad } from "./$types"
|
||||
|
||||
import { ChangelistsPage_ChangelistsQuery } from "./page.gql"
|
||||
|
||||
const PAGE_SIZE = 20
|
||||
|
||||
export const load: PageLoad = ({ parent, params }) => {
|
||||
const client = getGraphQLClient()
|
||||
const { repoName, revision = '' } = parseRepoRevision(params.repo)
|
||||
const path = params.path ? decodeURIComponent(params.path) : ''
|
||||
const resolvedRevision = resolveRevision(parent, revision)
|
||||
|
||||
const changelistsQuery = infinityQuery({
|
||||
client,
|
||||
query: ChangelistsPage_ChangelistsQuery,
|
||||
variables: resolvedRevision.then(revision => ({
|
||||
depotName: repoName,
|
||||
revision,
|
||||
first: PAGE_SIZE,
|
||||
path,
|
||||
afterCursor: null as string | null,
|
||||
})),
|
||||
map: result => {
|
||||
const ancestors = result.data?.repository?.commit?.ancestors
|
||||
return {
|
||||
nextVariables:
|
||||
ancestors?.pageInfo?.endCursor && ancestors?.pageInfo?.hasNextPage
|
||||
? { afterCursor: ancestors.pageInfo.endCursor }
|
||||
: undefined,
|
||||
data: ancestors?.nodes,
|
||||
error: result.error
|
||||
}
|
||||
},
|
||||
merge: (previous, next) => (previous ?? []).concat(next ?? []),
|
||||
createRestoreStrategy: api =>
|
||||
new IncrementalRestoreStrategy(
|
||||
api,
|
||||
n => n.length,
|
||||
n => ({ first: n.length })
|
||||
),
|
||||
})
|
||||
|
||||
return {
|
||||
changelistsQuery,
|
||||
path
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,35 @@
|
||||
fragment ChangelistsPage_GitCommitConnection on GitCommitConnection {
|
||||
nodes {
|
||||
canonicalURL
|
||||
oid
|
||||
abbreviatedOID
|
||||
externalURLs {
|
||||
serviceKind
|
||||
url
|
||||
}
|
||||
|
||||
...Changelist
|
||||
}
|
||||
pageInfo {
|
||||
hasNextPage
|
||||
endCursor
|
||||
}
|
||||
}
|
||||
|
||||
query ChangelistsPage_ChangelistsQuery(
|
||||
$depotName: String!
|
||||
$revision: String!
|
||||
$first: Int
|
||||
$path: String
|
||||
$afterCursor: String
|
||||
) {
|
||||
repository(name: $depotName) {
|
||||
id
|
||||
commit(rev: $revision) {
|
||||
id
|
||||
ancestors(first: $first, afterCursor: $afterCursor, path: $path) {
|
||||
...ChangelistsPage_GitCommitConnection
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -5,13 +5,12 @@
|
||||
import { navigating } from '$app/stores'
|
||||
import Commit from '$lib/Commit.svelte'
|
||||
import LoadingSpinner from '$lib/LoadingSpinner.svelte'
|
||||
import RepositoryRevPicker from '$lib/repo/RepositoryRevPicker.svelte'
|
||||
import { getHumanNameForCodeHost } from '$lib/repo/shared/codehost'
|
||||
import Scroller, { type Capture as ScrollerCapture } from '$lib/Scroller.svelte'
|
||||
import CodeHostIcon from '$lib/search/CodeHostIcon.svelte'
|
||||
import { Alert, Badge } from '$lib/wildcard'
|
||||
|
||||
import RepositoryRevPicker from '$lib/repo/RepositoryRevPicker.svelte'
|
||||
|
||||
import type { PageData, Snapshot } from './$types'
|
||||
|
||||
export let data: PageData
|
||||
|
||||
@ -115,10 +115,11 @@ export const load: LayoutLoad = async ({ params, url, depends }) => {
|
||||
),
|
||||
|
||||
// Depot pickers queries (changelists, @TODO: labels)
|
||||
getDepotChangelists: () =>
|
||||
getDepotChangelists: (searchTerm: string) =>
|
||||
client
|
||||
.query(DepotChangelists, {
|
||||
depotName: repoName,
|
||||
query: searchTerm,
|
||||
revision: resolvedRepository.commit?.oid || ''
|
||||
})
|
||||
.then(
|
||||
|
||||
Loading…
Reference in New Issue
Block a user