Browse Source

Updates graph search UI to use searchResults

- adds check to ensure commit is in graph
- moves input value validation to input handler
- removes mock state
main
Keith Daulton 2 years ago
committed by Eric Amodio
parent
commit
c9070ac224
4 changed files with 95 additions and 32 deletions
  1. +23
    -0
      src/plus/webviews/graph/graphWebview.ts
  2. +12
    -0
      src/plus/webviews/graph/protocol.ts
  3. +53
    -32
      src/webviews/apps/plus/graph/GraphWrapper.tsx
  4. +7
    -0
      src/webviews/apps/plus/graph/graph.tsx

+ 23
- 0
src/plus/webviews/graph/graphWebview.ts View File

@ -36,8 +36,10 @@ import {
DidChangeNotificationType,
DidChangeSelectionNotificationType,
DidChangeSubscriptionNotificationType,
DidEnsureCommitNotificationType,
DidSearchCommitsNotificationType,
DismissBannerCommandType,
EnsureHasCommitCommandType,
GetMissingAvatarsCommandType,
GetMoreCommitsCommandType,
SearchCommitsCommandType,
@ -211,6 +213,9 @@ export class GraphWebview extends WebviewBase {
case UpdateSelectionCommandType.method:
onIpc(UpdateSelectionCommandType, e, params => this.onSelectionChanged(params.selection));
break;
case EnsureHasCommitCommandType.method:
onIpc(EnsureHasCommitCommandType, e, params => this.onEnsureCommit(params.id, e.id));
break;
}
}
@ -673,6 +678,24 @@ export class GraphWebview extends WebviewBase {
this._selectedSha = sha;
this._selectedRows = sha != null ? { [sha]: true } : {};
}
private async onEnsureCommit(id: string, completionId: string) {
if (this._graph == null) return;
if (!this._graph.ids.has(id)) {
const { defaultItemLimit, pageItemLimit } = configuration.get('graph');
const newGraph = await this._graph.more!(pageItemLimit ?? defaultItemLimit, id);
if (newGraph != null) {
this.setGraph(newGraph);
} else {
debugger;
}
void this.notifyDidChangeCommits();
}
void this.notify(DidEnsureCommitNotificationType, { id: id }, completionId);
}
}
function formatRepositories(repositories: Repository[]): GraphRepository[] {

+ 12
- 0
src/plus/webviews/graph/protocol.ts View File

@ -112,6 +112,11 @@ export interface UpdateSelectionParams {
}
export const UpdateSelectionCommandType = new IpcCommandType<UpdateSelectionParams>('graph/update/selection');
export interface EnsureHasCommitParams {
id: string;
}
export const EnsureHasCommitCommandType = new IpcCommandType<EnsureHasCommitParams>('graph/update/ensureHasCommit');
// Notifications
export interface DidChangeParams {
state: State;
@ -164,3 +169,10 @@ export interface DidSearchCommitsParams {
export const DidSearchCommitsNotificationType = new IpcNotificationType<DidSearchCommitsParams>(
'graph/commits/didSearch',
);
export interface DidEnsureCommitParams {
id?: string;
}
export const DidEnsureCommitNotificationType = new IpcNotificationType<DidEnsureCommitParams>(
'graph/commits/didEnsureCommit',
);

+ 53
- 32
src/webviews/apps/plus/graph/GraphWrapper.tsx View File

@ -16,6 +16,7 @@ import { RepositoryVisibility } from '../../../../git/gitProvider';
import type { GitGraphRowType } from '../../../../git/models/graph';
import type { SearchPattern } from '../../../../git/search';
import type {
DidEnsureCommitParams,
DismissBannerParams,
GraphComponentConfig,
GraphRepository,
@ -39,6 +40,7 @@ export interface GraphWrapperProps extends State {
onSearchCommits?: (search: SearchPattern) => void; //Promise<DidSearchCommitsParams>;
onDismissBanner?: (key: DismissBannerParams['key']) => void;
onSelectionChange?: (selection: { id: string; type: GitGraphRowType }[]) => void;
onEnsureCommit?: (id: string) => Promise<DidEnsureCommitParams>;
}
const getStyleProps = (
@ -91,11 +93,11 @@ const getGraphDateFormatter = (config?: GraphComponentConfig): OnFormatCommitDat
return (commitDateTime: number) => formatCommitDateTime(commitDateTime, config?.dateStyle, config?.dateFormat);
};
const getSearchHighlights = (searchResults: State['searchResults']): { [id: string]: boolean } | undefined => {
if (!searchResults?.ids?.length) return undefined;
const getSearchHighlights = (searchIds?: string[]): { [id: string]: boolean } | undefined => {
if (!searchIds?.length) return undefined;
const highlights: { [id: string]: boolean } = {};
for (const sha of searchResults.ids) {
for (const sha of searchIds) {
highlights[sha] = true;
}
return highlights;
@ -205,9 +207,10 @@ export function GraphWrapper({
nonce,
mixedColumnColors,
previewBanner = true,
searchResults: searchResults2,
searchResults,
trialBanner = true,
onDismissBanner,
onEnsureCommit,
}: GraphWrapperProps) {
const [graphRows, setGraphRows] = useState(rows);
const [graphAvatars, setAvatars] = useState(avatars);
@ -240,58 +243,76 @@ export function GraphWrapper({
const [columnSettingsExpanded, setColumnSettingsExpanded] = useState(false);
// search state
const [searchValue, setSearchValue] = useState('');
const [searchResults, setSearchResults] = useState<GraphRow[]>([]);
const [searchResultKey, setSearchResultKey] = useState<string | undefined>(undefined);
const [searchHighlights, setSearchHighlights] = useState(getSearchHighlights(searchResults2));
const [searchIds, setSearchIds] = useState(searchResults?.ids);
useEffect(() => {
if (searchValue === '' || searchValue.length < 3 || graphRows.length < 1) {
setSearchResults([]);
if (graphRows.length === 0) {
setSearchIds(undefined);
}
}, [graphRows]);
useEffect(() => {
if (searchIds == null) {
setSearchResultKey(undefined);
setSearchHighlights(undefined);
return;
}
const results = graphRows.filter(row => row.message.toLowerCase().indexOf(searchValue.toLowerCase()) > 0);
setSearchResults(results);
if (
searchResultKey == null ||
(searchResultKey != null && results.findIndex(row => row.sha === searchResultKey) === -1)
) {
setSearchResultKey(results[0]?.sha);
if (searchResultKey == null || (searchResultKey != null && searchIds.includes(searchResultKey))) {
setSearchResultKey(searchIds[0]);
}
}, [searchValue, graphRows]);
}, [searchIds]);
const searchHighlights = useMemo(() => getSearchHighlights(searchIds), [searchIds]);
const searchPosition: number = useMemo(() => {
if (searchResultKey == null) {
return 1;
if (searchResultKey == null || searchIds == null) {
return 0;
}
const idx = searchResults.findIndex(row => row.sha === searchResultKey);
const idx = searchIds.indexOf(searchResultKey);
if (idx < 1) {
return 1;
}
return idx + 1;
}, [searchResultKey, searchResults]);
}, [searchResultKey, searchIds]);
const handleSearchNavigation = (next = true) => {
const rowIndex = searchResultKey != null && searchResults.findIndex(row => row.sha === searchResultKey);
if (rowIndex === false) {
return;
}
if (next && rowIndex < searchResults.length - 1) {
setSearchResultKey(searchResults[rowIndex + 1].sha);
if (searchResultKey == null || searchIds == null) return;
const rowIndex = searchIds.indexOf(searchResultKey);
if (rowIndex === -1) return;
let nextSha: string | undefined;
if (next && rowIndex < searchIds.length - 1) {
nextSha = searchIds[rowIndex + 1];
} else if (!next && rowIndex > 0) {
setSearchResultKey(searchResults[rowIndex - 1].sha);
nextSha = searchIds[rowIndex - 1];
}
if (nextSha == null) return;
if (onEnsureCommit != null) {
setIsLoading(true);
onEnsureCommit(nextSha).finally(() => {
setIsLoading(false);
setSearchResultKey(nextSha);
});
} else {
setSearchResultKey(nextSha);
}
};
const handleSearchInput = (e: FormEvent<HTMLInputElement>) => {
const currentValue = e.currentTarget.value;
setSearchValue(currentValue);
if (currentValue.length < 3) {
setSearchResultKey(undefined);
setSearchIds(undefined);
return;
}
onSearchCommits?.({ pattern: currentValue });
};
@ -327,7 +348,7 @@ export function GraphWrapper({
setSubscriptionSnapshot(state.subscription);
setIsPrivateRepo(state.selectedRepositoryVisibility === RepositoryVisibility.Private);
setIsLoading(state.loading);
setSearchHighlights(getSearchHighlights(state.searchResults));
setSearchIds(state.searchResults?.ids);
}
useEffect(() => subscriber?.(transformData), []);
@ -568,7 +589,7 @@ export function GraphWrapper({
<SearchNav
aria-label="Graph search navigation"
step={searchPosition}
total={searchResults.length}
total={searchIds?.length ?? 0}
onPrevious={() => handleSearchNavigation(false)}
onNext={() => handleSearchNavigation(true)}
/>

+ 7
- 0
src/webviews/apps/plus/graph/graph.tsx View File

@ -18,8 +18,10 @@ import {
DidChangeNotificationType,
DidChangeSelectionNotificationType,
DidChangeSubscriptionNotificationType,
DidEnsureCommitNotificationType,
DidSearchCommitsNotificationType,
DismissBannerCommandType,
EnsureHasCommitCommandType,
GetMissingAvatarsCommandType,
GetMoreCommitsCommandType,
SearchCommitsCommandType,
@ -81,6 +83,7 @@ export class GraphApp extends App {
250,
)}
onDismissBanner={key => this.onDismissBanner(key)}
onEnsureCommit={this.onEnsureCommit.bind(this)}
{...this.state}
/>,
$root,
@ -309,6 +312,10 @@ export class GraphApp extends App {
return this.sendCommand(SearchCommitsCommandType, { search: search });
}
private onEnsureCommit(id: string) {
return this.sendCommandWithCompletion(EnsureHasCommitCommandType, { id: id }, DidEnsureCommitNotificationType);
}
private onSelectionChanged(selection: { id: string; type: GitGraphRowType }[]) {
this.sendCommand(UpdateSelectionCommandType, {
selection: selection,

Loading…
Cancel
Save