addons/isl/src/ChangedFilesWithFetching.tsxblame
View source
b69ab311/**
b69ab312 * Copyright (c) Meta Platforms, Inc. and affiliates.
b69ab313 *
b69ab314 * This source code is licensed under the MIT license found in the
b69ab315 * LICENSE file in the root directory of this source tree.
b69ab316 */
b69ab317
b69ab318import type {ChangedFile, CommitInfo, FilesSample, Hash, Result} from './types';
b69ab319
b69ab3110import {Button} from 'isl-components/Button';
b69ab3111import {Tooltip} from 'isl-components/Tooltip';
b69ab3112import {useEffect, useState} from 'react';
b69ab3113import {CancellationToken} from 'shared/CancellationToken';
b69ab3114import {ComparisonType} from 'shared/Comparison';
b69ab3115import {LRU} from 'shared/LRU';
b69ab3116import serverAPI from './ClientToServerAPI';
b69ab3117import {ChangedFiles} from './UncommittedChanges';
b69ab3118import {t, T} from './i18n';
b69ab3119
b69ab3120// Cache fetches in progress so we don't double fetch
b69ab3121const commitFilesCache = new LRU<Hash, Promise<Result<FilesSample>>>(10);
b69ab3122export const __TEST__ = {commitFilesCache};
b69ab3123
b69ab3124/**
b69ab3125 * The basic CommitInfo we fetch in bulk only contains the first 25 files,
b69ab3126 * and is missing file statuses.
b69ab3127 * But we want to be able to scroll through pages of files,
b69ab3128 * and also see their statuses (added, removed, etc).
b69ab3129 * So all files for the currently selected commit,
b69ab3130 * to augment the subset we already have.
b69ab3131 * Public commits don't show changed files by default for performance,
b69ab3132 * but instead show a button used to fetch all the files.
b69ab3133 */
b69ab3134export function ChangedFilesWithFetching({commit}: {commit: CommitInfo}) {
b69ab3135 const [fetchedAllFiles, setFetchedAllFiles] = useState<FilesSample | undefined>(undefined);
b69ab3136
b69ab3137 const [showingPublicWarning, setShowPublicWarning] = useState(commit.phase === 'public');
b69ab3138 useEffect(() => {
b69ab3139 setShowPublicWarning(commit.phase === 'public');
b69ab3140 }, [commit.hash, commit.phase]);
b69ab3141
b69ab3142 useEffect(() => {
b69ab3143 if (showingPublicWarning === true) {
b69ab3144 return;
b69ab3145 }
b69ab3146
b69ab3147 setFetchedAllFiles(undefined);
b69ab3148 const cancel = new CancellationToken();
b69ab3149 getChangedFilesForHash(commit.hash, undefined).then(result => {
b69ab3150 if (cancel.isCancelled) {
b69ab3151 return;
b69ab3152 }
b69ab3153 if (result.value != null) {
b69ab3154 setFetchedAllFiles(result.value);
b69ab3155 }
b69ab3156 });
b69ab3157 return () => {
b69ab3158 cancel.cancel();
b69ab3159 };
b69ab3160 }, [commit.hash, showingPublicWarning]);
b69ab3161
b69ab3162 if (showingPublicWarning) {
b69ab3163 return (
b69ab3164 <Tooltip
b69ab3165 title={t(
b69ab3166 'Changed files are not loaded for public commits by default, for performance. Click to load changed files.',
b69ab3167 )}>
b69ab3168 <Button onClick={() => setShowPublicWarning(false)}>
b69ab3169 <T>Load changed files</T>
b69ab3170 </Button>
b69ab3171 </Tooltip>
b69ab3172 );
b69ab3173 }
b69ab3174
b69ab3175 return (
b69ab3176 <ChangedFiles
b69ab3177 filesSubset={
b69ab3178 fetchedAllFiles?.filesSample ??
b69ab3179 commit.filePathsSample.map(
b69ab3180 (filePath): ChangedFile => ({
b69ab3181 path: filePath,
b69ab3182 // default to 'modified' as a best guess.
b69ab3183 // TODO: should this be a special loading status that shows a spinner?
b69ab3184 status: 'M' as const,
b69ab3185 }),
b69ab3186 )
b69ab3187 }
b69ab3188 totalFiles={fetchedAllFiles?.totalFileCount ?? commit.totalFileCount}
b69ab3189 comparison={
b69ab3190 commit.isDot
b69ab3191 ? {type: ComparisonType.HeadChanges}
b69ab3192 : {
b69ab3193 type: ComparisonType.Committed,
b69ab3194 hash: commit.hash,
b69ab3195 }
b69ab3196 }
b69ab3197 />
b69ab3198 );
b69ab3199}
b69ab31100
b69ab31101/**
b69ab31102 * Get changed files in a given commit.
b69ab31103 * A small subset of the files may have already been fetched,
b69ab31104 * or in some cases no files may be cached yet and all files need to be fetched asynchronously. */
b69ab31105export function getChangedFilesForHash(
b69ab31106 hash: Hash,
b69ab31107 limit?: number | undefined,
b69ab31108): Promise<Result<FilesSample>> {
b69ab31109 const foundPromise = commitFilesCache.get(hash);
b69ab31110 if (foundPromise != null) {
b69ab31111 return foundPromise;
b69ab31112 }
b69ab31113 serverAPI.postMessage({
b69ab31114 type: 'fetchCommitChangedFiles',
b69ab31115 hash,
b69ab31116 limit,
b69ab31117 });
b69ab31118
b69ab31119 const resultPromise = serverAPI
b69ab31120 .nextMessageMatching('fetchedCommitChangedFiles', message => message.hash === hash)
b69ab31121 .then(result => result.result);
b69ab31122 commitFilesCache.set(hash, resultPromise);
b69ab31123
b69ab31124 return resultPromise;
b69ab31125}