mirror of
https://github.com/sourcebot-dev/sourcebot.git
synced 2025-12-12 20:35:24 +00:00
fix filter
This commit is contained in:
parent
09581f3cc2
commit
b09def9ddd
2 changed files with 43 additions and 35 deletions
|
|
@ -1,10 +1,11 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { RepositoryInfo, SearchResultFile } from "@/features/search/types";
|
import { RepositoryInfo, SearchResultFile } from "@/features/search/types";
|
||||||
import { FileMatchContainer, MAX_MATCHES_TO_PREVIEW } from "./fileMatchContainer";
|
|
||||||
import { useVirtualizer, VirtualItem } from "@tanstack/react-virtual";
|
import { useVirtualizer, VirtualItem } from "@tanstack/react-virtual";
|
||||||
import { useCallback, useEffect, useImperativeHandle, useRef, useState, forwardRef } from "react";
|
|
||||||
import { useDebounce } from "@uidotdev/usehooks";
|
import { useDebounce } from "@uidotdev/usehooks";
|
||||||
|
import { forwardRef, useCallback, useEffect, useImperativeHandle, useRef } from "react";
|
||||||
|
import { useMap } from "usehooks-ts";
|
||||||
|
import { FileMatchContainer, MAX_MATCHES_TO_PREVIEW } from "./fileMatchContainer";
|
||||||
|
|
||||||
interface SearchResultsPanelProps {
|
interface SearchResultsPanelProps {
|
||||||
fileMatches: SearchResultFile[];
|
fileMatches: SearchResultFile[];
|
||||||
|
|
@ -26,7 +27,15 @@ const ESTIMATED_MATCH_CONTAINER_HEIGHT_PX = 30;
|
||||||
type ScrollHistoryState = {
|
type ScrollHistoryState = {
|
||||||
scrollOffset?: number;
|
scrollOffset?: number;
|
||||||
measurementsCache?: VirtualItem[];
|
measurementsCache?: VirtualItem[];
|
||||||
showAllMatchesStates?: boolean[];
|
showAllMatchesMap?: [string, boolean][];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unique key for a given file match. Used to store the "show all matches" state for a
|
||||||
|
* given file match.
|
||||||
|
*/
|
||||||
|
const getFileMatchKey = (fileMatch: SearchResultFile) => {
|
||||||
|
return `${fileMatch.repository}-${fileMatch.fileName.text}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const SearchResultsPanel = forwardRef<SearchResultsPanelHandle, SearchResultsPanelProps>(({
|
export const SearchResultsPanel = forwardRef<SearchResultsPanelHandle, SearchResultsPanelProps>(({
|
||||||
|
|
@ -46,17 +55,17 @@ export const SearchResultsPanel = forwardRef<SearchResultsPanelHandle, SearchRes
|
||||||
const {
|
const {
|
||||||
scrollOffset: restoreOffset,
|
scrollOffset: restoreOffset,
|
||||||
measurementsCache: restoreMeasurementsCache,
|
measurementsCache: restoreMeasurementsCache,
|
||||||
showAllMatchesStates: restoreShowAllMatchesStates,
|
showAllMatchesMap: restoreShowAllMatchesStates,
|
||||||
} = history.state as ScrollHistoryState;
|
} = history.state as ScrollHistoryState;
|
||||||
|
|
||||||
const [showAllMatchesStates, setShowAllMatchesStates] = useState(restoreShowAllMatchesStates || Array(fileMatches.length).fill(false));
|
const [showAllMatchesMap, showAllMatchesActions] = useMap<string, boolean>(restoreShowAllMatchesStates || []);
|
||||||
|
|
||||||
const virtualizer = useVirtualizer({
|
const virtualizer = useVirtualizer({
|
||||||
count: fileMatches.length,
|
count: fileMatches.length,
|
||||||
getScrollElement: () => parentRef.current,
|
getScrollElement: () => parentRef.current,
|
||||||
estimateSize: (index) => {
|
estimateSize: (index) => {
|
||||||
const fileMatch = fileMatches[index];
|
const fileMatch = fileMatches[index];
|
||||||
const showAllMatches = showAllMatchesStates[index];
|
const showAllMatches = showAllMatchesMap.get(getFileMatchKey(fileMatch));
|
||||||
|
|
||||||
// Quick guesstimation ;) This needs to be quick since the virtualizer will
|
// Quick guesstimation ;) This needs to be quick since the virtualizer will
|
||||||
// run this upfront for all items in the list.
|
// run this upfront for all items in the list.
|
||||||
|
|
@ -78,7 +87,6 @@ export const SearchResultsPanel = forwardRef<SearchResultsPanelHandle, SearchRes
|
||||||
});
|
});
|
||||||
|
|
||||||
const resetScroll = useCallback(() => {
|
const resetScroll = useCallback(() => {
|
||||||
setShowAllMatchesStates(Array(fileMatches.length).fill(false));
|
|
||||||
virtualizer.scrollToIndex(0);
|
virtualizer.scrollToIndex(0);
|
||||||
}, [fileMatches.length, virtualizer]);
|
}, [fileMatches.length, virtualizer]);
|
||||||
|
|
||||||
|
|
@ -89,24 +97,22 @@ export const SearchResultsPanel = forwardRef<SearchResultsPanelHandle, SearchRes
|
||||||
|
|
||||||
|
|
||||||
// Save the scroll state to the history stack.
|
// Save the scroll state to the history stack.
|
||||||
const debouncedScrollOffset = useDebounce(virtualizer.scrollOffset, 100);
|
const debouncedScrollOffset = useDebounce(virtualizer.scrollOffset, 500);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
history.replaceState(
|
history.replaceState(
|
||||||
{
|
{
|
||||||
scrollOffset: debouncedScrollOffset ?? undefined,
|
scrollOffset: debouncedScrollOffset ?? undefined,
|
||||||
measurementsCache: virtualizer.measurementsCache,
|
measurementsCache: virtualizer.measurementsCache,
|
||||||
showAllMatchesStates,
|
showAllMatchesMap: Array.from(showAllMatchesMap.entries()),
|
||||||
} satisfies ScrollHistoryState,
|
} satisfies ScrollHistoryState,
|
||||||
'',
|
'',
|
||||||
window.location.href
|
window.location.href
|
||||||
);
|
);
|
||||||
}, [debouncedScrollOffset, virtualizer.measurementsCache, showAllMatchesStates]);
|
}, [debouncedScrollOffset, virtualizer.measurementsCache, showAllMatchesMap]);
|
||||||
|
|
||||||
const onShowAllMatchesButtonClicked = useCallback((index: number) => {
|
const onShowAllMatchesButtonClicked = useCallback((fileMatchKey: string, index: number) => {
|
||||||
const states = [...showAllMatchesStates];
|
const wasShown = showAllMatchesMap.get(fileMatchKey) ?? false;
|
||||||
const wasShown = states[index];
|
showAllMatchesActions.set(fileMatchKey, !wasShown);
|
||||||
states[index] = !wasShown;
|
|
||||||
setShowAllMatchesStates(states);
|
|
||||||
|
|
||||||
// When collapsing, scroll to the top of the file match container. This ensures
|
// When collapsing, scroll to the top of the file match container. This ensures
|
||||||
// that the focused "show fewer matches" button is visible.
|
// that the focused "show fewer matches" button is visible.
|
||||||
|
|
@ -115,7 +121,7 @@ export const SearchResultsPanel = forwardRef<SearchResultsPanelHandle, SearchRes
|
||||||
align: 'start'
|
align: 'start'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, [showAllMatchesStates, virtualizer]);
|
}, [showAllMatchesMap, virtualizer]);
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
@ -155,9 +161,9 @@ export const SearchResultsPanel = forwardRef<SearchResultsPanelHandle, SearchRes
|
||||||
onOpenFilePreview={(matchIndex) => {
|
onOpenFilePreview={(matchIndex) => {
|
||||||
onOpenFilePreview(file, matchIndex);
|
onOpenFilePreview(file, matchIndex);
|
||||||
}}
|
}}
|
||||||
showAllMatches={showAllMatchesStates[virtualRow.index]}
|
showAllMatches={showAllMatchesMap.get(getFileMatchKey(file)) ?? false}
|
||||||
onShowAllMatchesButtonClicked={() => {
|
onShowAllMatchesButtonClicked={() => {
|
||||||
onShowAllMatchesButtonClicked(virtualRow.index);
|
onShowAllMatchesButtonClicked(getFileMatchKey(file), virtualRow.index);
|
||||||
}}
|
}}
|
||||||
isBranchFilteringEnabled={isBranchFilteringEnabled}
|
isBranchFilteringEnabled={isBranchFilteringEnabled}
|
||||||
repoInfo={repoInfo}
|
repoInfo={repoInfo}
|
||||||
|
|
|
||||||
|
|
@ -1,24 +1,25 @@
|
||||||
import { Tree, SyntaxNode } from "@sourcebot/query-language";
|
|
||||||
import { Q } from '@/proto/zoekt/webserver/v1/Q';
|
import { Q } from '@/proto/zoekt/webserver/v1/Q';
|
||||||
import {
|
import {
|
||||||
Program,
|
|
||||||
AndExpr,
|
AndExpr,
|
||||||
OrExpr,
|
ArchivedExpr,
|
||||||
NegateExpr,
|
|
||||||
ParenExpr,
|
|
||||||
PrefixExpr,
|
|
||||||
Term,
|
|
||||||
FileExpr,
|
|
||||||
RepoExpr,
|
|
||||||
RevisionExpr,
|
|
||||||
ContentExpr,
|
ContentExpr,
|
||||||
ContextExpr,
|
ContextExpr,
|
||||||
LangExpr,
|
FileExpr,
|
||||||
SymExpr,
|
|
||||||
ArchivedExpr,
|
|
||||||
ForkExpr,
|
ForkExpr,
|
||||||
VisibilityExpr,
|
LangExpr,
|
||||||
|
NegateExpr,
|
||||||
|
OrExpr,
|
||||||
|
ParenExpr,
|
||||||
|
PrefixExpr,
|
||||||
|
Program,
|
||||||
|
RepoExpr,
|
||||||
RepoSetExpr,
|
RepoSetExpr,
|
||||||
|
RevisionExpr,
|
||||||
|
SymExpr,
|
||||||
|
SyntaxNode,
|
||||||
|
Term,
|
||||||
|
Tree,
|
||||||
|
VisibilityExpr,
|
||||||
} from '@sourcebot/query-language';
|
} from '@sourcebot/query-language';
|
||||||
|
|
||||||
type ArchivedValue = 'yes' | 'no' | 'only';
|
type ArchivedValue = 'yes' | 'no' | 'only';
|
||||||
|
|
@ -227,12 +228,13 @@ export const transformToZoektQuery = ({
|
||||||
|
|
||||||
const flags: ('FLAG_ONLY_PUBLIC' | 'FLAG_ONLY_PRIVATE')[] = [];
|
const flags: ('FLAG_ONLY_PUBLIC' | 'FLAG_ONLY_PRIVATE')[] = [];
|
||||||
|
|
||||||
if (rawValue === 'public') {
|
if (rawValue === 'any') {
|
||||||
|
// 'any' means no filter
|
||||||
|
} else if (rawValue === 'public') {
|
||||||
flags.push('FLAG_ONLY_PUBLIC');
|
flags.push('FLAG_ONLY_PUBLIC');
|
||||||
} else if (rawValue === 'private') {
|
} else if (rawValue === 'private') {
|
||||||
flags.push('FLAG_ONLY_PRIVATE');
|
flags.push('FLAG_ONLY_PRIVATE');
|
||||||
}
|
}
|
||||||
// 'any' means no filter
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
raw_config: {
|
raw_config: {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue