mirror of
https://seed.radicle.dev/rad:z4V1sjrXqjvFdnCUbxPFqd5p4DtH5.git
synced 2026-10-26 23:20:35 +08:00
Implement loading on scroll for patch second column
This commit is contained in:
@@ -9,33 +9,29 @@
|
||||
patchStatusColor,
|
||||
} from "@app/lib/utils";
|
||||
import { invoke } from "@app/lib/invoke";
|
||||
import { onMount } from "svelte";
|
||||
import { push } from "@app/lib/router";
|
||||
|
||||
import DiffStatBadge from "./DiffStatBadge.svelte";
|
||||
import Icon from "./Icon.svelte";
|
||||
import Id from "./Id.svelte";
|
||||
import InlineTitle from "./InlineTitle.svelte";
|
||||
import NodeId from "./NodeId.svelte";
|
||||
import Id from "./Id.svelte";
|
||||
|
||||
let stats: Stats | undefined = $state(undefined);
|
||||
|
||||
onMount(async () => {
|
||||
stats = await invoke<Stats>("diff_stats", {
|
||||
rid,
|
||||
base: patch.base,
|
||||
head: patch.head,
|
||||
});
|
||||
});
|
||||
|
||||
interface Props {
|
||||
patch: Patch;
|
||||
rid: string;
|
||||
selected?: boolean;
|
||||
compact?: boolean;
|
||||
loadPatch?: (rid: string, patchId: string) => void;
|
||||
}
|
||||
|
||||
const { patch, rid, selected = false, compact = false }: Props = $props();
|
||||
const {
|
||||
patch,
|
||||
rid,
|
||||
selected = false,
|
||||
compact = false,
|
||||
loadPatch,
|
||||
}: Props = $props();
|
||||
</script>
|
||||
|
||||
<style>
|
||||
@@ -79,7 +75,11 @@
|
||||
class:selected
|
||||
class="patch-teaser"
|
||||
onclick={() => {
|
||||
void push({ resource: "repo.patch", rid, patch: patch.id });
|
||||
if (loadPatch) {
|
||||
loadPatch(rid, patch.id);
|
||||
} else {
|
||||
void push({ resource: "repo.patch", rid, patch: patch.id });
|
||||
}
|
||||
}}>
|
||||
<div class="global-flex">
|
||||
<div
|
||||
@@ -104,9 +104,10 @@
|
||||
|
||||
{#if !compact}
|
||||
<div class="global-flex">
|
||||
{#if stats}
|
||||
{#await invoke<Stats>( "diff_stats", { rid, base: patch.base, head: patch.head }, ) then stats}
|
||||
<DiffStatBadge {stats} />
|
||||
{/if}
|
||||
{/await}
|
||||
|
||||
{#each patch.labels as label}
|
||||
<div class="global-counter txt-small">{label}</div>
|
||||
{/each}
|
||||
|
||||
@@ -71,6 +71,7 @@
|
||||
|
||||
{#if activeTab === "patches"}
|
||||
<Border
|
||||
styleCursor="pointer"
|
||||
onclick={() => {
|
||||
void router.push({
|
||||
resource: "repo.patches",
|
||||
|
||||
@@ -13,7 +13,8 @@
|
||||
headerCenter?: Snippet;
|
||||
secondColumn: Snippet;
|
||||
sidebar: Snippet;
|
||||
loadMore?: () => Promise<void>;
|
||||
loadMoreContent?: () => Promise<void>;
|
||||
loadMoreSecondColumn?: () => Promise<void>;
|
||||
hideSidebar?: boolean;
|
||||
styleSecondColumnOverflow?: string;
|
||||
}
|
||||
@@ -24,7 +25,8 @@
|
||||
headerCenter = undefined,
|
||||
secondColumn,
|
||||
sidebar,
|
||||
loadMore = undefined,
|
||||
loadMoreContent = undefined,
|
||||
loadMoreSecondColumn = undefined,
|
||||
hideSidebar = false,
|
||||
styleSecondColumnOverflow = "scroll",
|
||||
}: Props = $props();
|
||||
@@ -34,8 +36,10 @@
|
||||
let oneColumnLayout = $state(
|
||||
localStorage ? localStorage.getItem(LAYOUT_KEY) === "one-column" : false,
|
||||
);
|
||||
let listElement: HTMLElement | undefined = $state();
|
||||
let loading = false;
|
||||
let contentContainer: HTMLElement | undefined = $state();
|
||||
let secondColumnContainer: HTMLElement | undefined = $state();
|
||||
let loadingContent = false;
|
||||
let loadingSecondColumn = false;
|
||||
|
||||
function storeLayout(newValue: LayoutState): void {
|
||||
oneColumnLayout = newValue === "one-column";
|
||||
@@ -49,16 +53,33 @@
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
if (listElement && loadMore) {
|
||||
listElement.addEventListener("scroll", async () => {
|
||||
if (contentContainer && loadMoreContent) {
|
||||
contentContainer.addEventListener("scroll", async () => {
|
||||
if (
|
||||
listElement &&
|
||||
listElement.scrollTop + listElement.clientHeight >=
|
||||
listElement.scrollHeight - 600 &&
|
||||
loading === false
|
||||
contentContainer &&
|
||||
contentContainer.scrollTop + contentContainer.clientHeight >=
|
||||
contentContainer.scrollHeight - 600 &&
|
||||
loadingContent === false
|
||||
) {
|
||||
loading = true;
|
||||
void loadMore().finally(() => (loading = false));
|
||||
loadingContent = true;
|
||||
void loadMoreContent().finally(() => (loadingContent = false));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (secondColumnContainer && loadMoreSecondColumn) {
|
||||
secondColumnContainer.addEventListener("scroll", async () => {
|
||||
if (
|
||||
secondColumnContainer &&
|
||||
secondColumnContainer.scrollTop +
|
||||
secondColumnContainer.clientHeight >=
|
||||
secondColumnContainer.scrollHeight - 600 &&
|
||||
loadingSecondColumn === false
|
||||
) {
|
||||
loadingSecondColumn = true;
|
||||
void loadMoreSecondColumn().finally(
|
||||
() => (loadingSecondColumn = false),
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -93,7 +114,7 @@
|
||||
.secondColumn {
|
||||
grid-column: 2 / 3;
|
||||
margin: 1rem 0 0 0;
|
||||
max-width: 30rem;
|
||||
max-width: 28rem;
|
||||
min-width: 14rem;
|
||||
margin-right: 1rem;
|
||||
}
|
||||
@@ -162,6 +183,7 @@
|
||||
|
||||
<div
|
||||
class="secondColumn"
|
||||
bind:this={secondColumnContainer}
|
||||
style:display={oneColumnLayout ? "none" : undefined}
|
||||
style:overflow={styleSecondColumnOverflow}>
|
||||
{@render secondColumn()}
|
||||
@@ -169,7 +191,7 @@
|
||||
|
||||
<div
|
||||
class="content global-reset-scroll-after-navigate"
|
||||
bind:this={listElement}>
|
||||
bind:this={contentContainer}>
|
||||
{@render children()}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
<script lang="ts">
|
||||
import type { Config } from "@bindings/config/Config";
|
||||
import type { PaginatedQuery } from "@bindings/cob/PaginatedQuery";
|
||||
import type { Patch } from "@bindings/cob/patch/Patch";
|
||||
import type { RepoInfo } from "@bindings/repo/RepoInfo";
|
||||
import type { Revision } from "@bindings/cob/patch/Revision";
|
||||
|
||||
import { invoke } from "@app/lib/invoke";
|
||||
|
||||
import CommentComponent from "@app/components/Comment.svelte";
|
||||
import CopyableId from "@app/components/CopyableId.svelte";
|
||||
import Icon from "@app/components/Icon.svelte";
|
||||
@@ -18,12 +21,49 @@
|
||||
interface Props {
|
||||
repo: RepoInfo;
|
||||
patch: Patch;
|
||||
patches: Patch[];
|
||||
patches: PaginatedQuery<Patch[]>;
|
||||
revisions: Revision[];
|
||||
config: Config;
|
||||
}
|
||||
|
||||
const { repo, patch, patches, revisions, config }: Props = $props();
|
||||
/* eslint-disable prefer-const */
|
||||
let { repo, patch, patches, revisions, config }: Props = $props();
|
||||
/* eslint-enable prefer-const */
|
||||
|
||||
let items = $state(patches.content);
|
||||
let cursor = patches.cursor;
|
||||
let more = patches.more;
|
||||
|
||||
$effect(() => {
|
||||
items = patches.content;
|
||||
cursor = patches.cursor;
|
||||
more = patches.more;
|
||||
});
|
||||
|
||||
async function loadPatch(rid: string, patchId: string) {
|
||||
patch = await invoke<Patch>("patch_by_id", {
|
||||
rid: rid,
|
||||
id: patchId,
|
||||
});
|
||||
revisions = await invoke<Revision[]>("revisions_by_patch", {
|
||||
rid: rid,
|
||||
id: patchId,
|
||||
});
|
||||
}
|
||||
|
||||
async function loadMoreSecondColumn() {
|
||||
if (more) {
|
||||
const p = await invoke<PaginatedQuery<Patch[]>>("list_patches", {
|
||||
rid: repo.rid,
|
||||
skip: cursor + 20,
|
||||
take: 20,
|
||||
});
|
||||
|
||||
cursor = p.cursor;
|
||||
more = p.more;
|
||||
items = [...items, ...p.content];
|
||||
}
|
||||
}
|
||||
|
||||
const project = $derived(repo.payloads["xyz.radicle.project"]!);
|
||||
</script>
|
||||
@@ -66,7 +106,7 @@
|
||||
}
|
||||
</style>
|
||||
|
||||
<Layout>
|
||||
<Layout {loadMoreSecondColumn}>
|
||||
{#snippet breadcrumbs()}
|
||||
<Link route={{ resource: "home" }}>
|
||||
<NodeId
|
||||
@@ -105,9 +145,10 @@
|
||||
Patches
|
||||
</div>
|
||||
<div class="patch-list">
|
||||
{#each patches as p}
|
||||
{#each items as p}
|
||||
<PatchTeaser
|
||||
compact
|
||||
{loadPatch}
|
||||
patch={p}
|
||||
rid={repo.rid}
|
||||
selected={patch && p.id === patch.id} />
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
more = patches.more;
|
||||
});
|
||||
|
||||
async function loadMore() {
|
||||
async function loadMoreContent() {
|
||||
if (more) {
|
||||
const p = await invoke<PaginatedQuery<Patch[]>>("list_patches", {
|
||||
rid: repo.rid,
|
||||
@@ -71,7 +71,7 @@
|
||||
}
|
||||
</style>
|
||||
|
||||
<Layout {loadMore} hideSidebar styleSecondColumnOverflow="visible">
|
||||
<Layout {loadMoreContent} hideSidebar styleSecondColumnOverflow="visible">
|
||||
{#snippet breadcrumbs()}
|
||||
<Link route={{ resource: "home" }}>
|
||||
<NodeId
|
||||
|
||||
@@ -74,7 +74,7 @@ export interface LoadedRepoPatchRoute {
|
||||
repo: RepoInfo;
|
||||
config: Config;
|
||||
patch: Patch;
|
||||
patches: Patch[];
|
||||
patches: PaginatedQuery<Patch[]>;
|
||||
revisions: Revision[];
|
||||
};
|
||||
}
|
||||
@@ -131,7 +131,7 @@ export async function loadPatch(
|
||||
|
||||
return {
|
||||
resource: "repo.patch",
|
||||
params: { repo, config, patch, patches: patches.content, revisions },
|
||||
params: { repo, config, patch, patches, revisions },
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user