mirror of
https://github.com/block/buzz.git
synced 2026-08-18 06:50:31 +02:00
fix(desktop): refine repository pagination
Signed-off-by: Evan Chen <evanc@squareup.com> Co-authored-by: Codex <noreply@openai.com> Ai-assisted: true
This commit is contained in:
@@ -9,8 +9,7 @@ use serde::Serialize;
|
||||
use std::time::UNIX_EPOCH;
|
||||
use tauri::State;
|
||||
|
||||
// Bound eager snapshot payloads without truncating the repository tree. Later
|
||||
// paths remain browseable without embedded content.
|
||||
// Bound eager content without truncating the repository tree.
|
||||
const MAX_EAGER_FILE_PREVIEWS: usize = 250;
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -260,16 +259,16 @@ fn parse_worktree_files(
|
||||
output
|
||||
.split('\0')
|
||||
.filter(|path| !path.trim().is_empty())
|
||||
.enumerate()
|
||||
.filter_map(|(index, path)| {
|
||||
.filter_map(|path| {
|
||||
let full_path = repo_dir.join(path);
|
||||
let metadata = std::fs::metadata(&full_path).ok()?;
|
||||
if !metadata.is_file() {
|
||||
return None;
|
||||
}
|
||||
metadata.is_file().then_some((path, full_path, metadata))
|
||||
})
|
||||
.enumerate()
|
||||
.map(|(index, (path, full_path, metadata))| {
|
||||
let size = Some(metadata.len());
|
||||
let latest_commit = latest_commit_by_path.get(path).cloned();
|
||||
Some(ProjectRepoFileInfo {
|
||||
ProjectRepoFileInfo {
|
||||
path: path.to_string(),
|
||||
kind: "blob".to_string(),
|
||||
size,
|
||||
@@ -281,7 +280,7 @@ fn parse_worktree_files(
|
||||
.map(|commit| commit.timestamp)
|
||||
.or_else(|| path_modified_at(&full_path)),
|
||||
latest_commit,
|
||||
})
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
@@ -325,21 +324,22 @@ fn parse_ls_tree(
|
||||
output: &str,
|
||||
latest_commit_by_path: &std::collections::HashMap<String, ProjectRepoCommitInfo>,
|
||||
) -> Vec<ProjectRepoFileInfo> {
|
||||
let mut blob_index = 0;
|
||||
output
|
||||
.lines()
|
||||
.enumerate()
|
||||
.filter_map(|(index, line)| {
|
||||
.filter_map(|line| {
|
||||
let (meta, path) = line.split_once('\t')?;
|
||||
let mut parts = meta.split_whitespace();
|
||||
let _mode = parts.next()?;
|
||||
let kind = parts.next()?.to_string();
|
||||
let _object = parts.next()?;
|
||||
let size = parts.next().and_then(|value| value.parse::<u64>().ok());
|
||||
let preview_content = if kind == "blob" && index < MAX_EAGER_FILE_PREVIEWS {
|
||||
read_preview_content(repo_dir, path, size)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
if kind == "blob" {
|
||||
blob_index += 1;
|
||||
}
|
||||
let preview_content = (kind == "blob" && blob_index <= MAX_EAGER_FILE_PREVIEWS)
|
||||
.then(|| read_preview_content(repo_dir, path, size))
|
||||
.flatten();
|
||||
Some(ProjectRepoFileInfo {
|
||||
path: path.to_string(),
|
||||
kind,
|
||||
|
||||
@@ -28,3 +28,54 @@ fn parse_ls_tree_keeps_paths_after_eager_preview_limit() {
|
||||
Some("src/application.rs")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_ls_tree_counts_only_blobs_toward_eager_preview_limit() {
|
||||
let repo_dir = tempfile::tempdir().expect("create temporary repository");
|
||||
std::fs::write(repo_dir.path().join("application.rs"), "fn main() {}")
|
||||
.expect("write preview file");
|
||||
let non_blob_entries = (0..MAX_EAGER_FILE_PREVIEWS)
|
||||
.map(|index| {
|
||||
format!(
|
||||
"160000 commit {} -\tvendor/dependency-{index:03}",
|
||||
"a".repeat(40)
|
||||
)
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n");
|
||||
let output = format!(
|
||||
"{non_blob_entries}\n100644 blob {} 12\tapplication.rs",
|
||||
"b".repeat(40)
|
||||
);
|
||||
|
||||
let files = parse_ls_tree(repo_dir.path(), &output, &std::collections::HashMap::new());
|
||||
|
||||
assert_eq!(
|
||||
files
|
||||
.last()
|
||||
.and_then(|file| file.preview_content.as_deref()),
|
||||
Some("fn main() {}")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_worktree_files_counts_only_files_toward_eager_preview_limit() {
|
||||
let repo_dir = tempfile::tempdir().expect("create temporary repository");
|
||||
std::fs::create_dir(repo_dir.path().join("directory")).expect("create directory");
|
||||
let paths = (0..MAX_EAGER_FILE_PREVIEWS)
|
||||
.map(|index| {
|
||||
let path = format!("file-{index:03}.txt");
|
||||
std::fs::write(repo_dir.path().join(&path), "preview").expect("write preview file");
|
||||
path
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let output = std::iter::once("directory")
|
||||
.chain(paths.iter().map(String::as_str))
|
||||
.collect::<Vec<_>>()
|
||||
.join("\0");
|
||||
|
||||
let files = parse_worktree_files(repo_dir.path(), &output, &std::collections::HashMap::new());
|
||||
|
||||
assert_eq!(files.len(), MAX_EAGER_FILE_PREVIEWS);
|
||||
assert!(files.iter().all(|file| file.preview_content.is_some()));
|
||||
}
|
||||
|
||||
@@ -649,6 +649,11 @@ export function RepositoryFilesPanel({
|
||||
[currentPath, files],
|
||||
);
|
||||
const visibleEntries = entries.slice(0, visibleEntryCount);
|
||||
const nextVisibleEntryCount = nextRepositoryEntryLimit(
|
||||
visibleEntryCount,
|
||||
entries.length,
|
||||
);
|
||||
const nextEntryCount = nextVisibleEntryCount - visibleEntries.length;
|
||||
const latestCommit = snapshot?.latestCommit ?? null;
|
||||
const knownLatestCommitProfile = React.useMemo(
|
||||
() => profileForCommitAuthor(latestCommit, profiles),
|
||||
@@ -918,10 +923,11 @@ export function RepositoryFilesPanel({
|
||||
</div>
|
||||
{entries.length > visibleEntries.length ? (
|
||||
<div className="flex items-center justify-between gap-3 border-border/50 border-t px-4 py-3 text-2xs text-muted-foreground">
|
||||
<span>
|
||||
<span aria-live="polite">
|
||||
Showing {visibleEntries.length} of {entries.length} entries.
|
||||
</span>
|
||||
<button
|
||||
aria-label={`Show next ${nextEntryCount} entries, ${nextVisibleEntryCount} of ${entries.length} total`}
|
||||
className="shrink-0 font-medium text-foreground hover:underline"
|
||||
onClick={() =>
|
||||
setVisibleEntryCount((current) =>
|
||||
@@ -930,11 +936,7 @@ export function RepositoryFilesPanel({
|
||||
}
|
||||
type="button"
|
||||
>
|
||||
Show next{" "}
|
||||
{Math.min(
|
||||
REPOSITORY_ENTRY_PAGE_SIZE,
|
||||
entries.length - visibleEntries.length,
|
||||
)}
|
||||
Show next {nextEntryCount}
|
||||
</button>
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
Reference in New Issue
Block a user