Merge pull request #1953 from github/robertbrignull/telemetry_ui
Report telemetry on actions taken in the UI
This commit is contained in:
@@ -413,7 +413,8 @@ export type FromRemoteQueriesMessage =
|
|||||||
| RemoteQueryDownloadAnalysisResultsMessage
|
| RemoteQueryDownloadAnalysisResultsMessage
|
||||||
| RemoteQueryDownloadAllAnalysesResultsMessage
|
| RemoteQueryDownloadAllAnalysesResultsMessage
|
||||||
| RemoteQueryExportResultsMessage
|
| RemoteQueryExportResultsMessage
|
||||||
| CopyRepoListMessage;
|
| CopyRepoListMessage
|
||||||
|
| TelemetryMessage;
|
||||||
|
|
||||||
export type ToRemoteQueriesMessage =
|
export type ToRemoteQueriesMessage =
|
||||||
| SetRemoteQueryResultMessage
|
| SetRemoteQueryResultMessage
|
||||||
@@ -504,6 +505,11 @@ export interface CancelVariantAnalysisMessage {
|
|||||||
t: "cancelVariantAnalysis";
|
t: "cancelVariantAnalysis";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface TelemetryMessage {
|
||||||
|
t: "telemetry";
|
||||||
|
action: string;
|
||||||
|
}
|
||||||
|
|
||||||
export type ToVariantAnalysisMessage =
|
export type ToVariantAnalysisMessage =
|
||||||
| SetVariantAnalysisMessage
|
| SetVariantAnalysisMessage
|
||||||
| SetRepoResultsMessage
|
| SetRepoResultsMessage
|
||||||
@@ -517,4 +523,5 @@ export type FromVariantAnalysisMessage =
|
|||||||
| CopyRepositoryListMessage
|
| CopyRepositoryListMessage
|
||||||
| ExportResultsMessage
|
| ExportResultsMessage
|
||||||
| OpenLogsMessage
|
| OpenLogsMessage
|
||||||
| CancelVariantAnalysisMessage;
|
| CancelVariantAnalysisMessage
|
||||||
|
| TelemetryMessage;
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ import { AnalysesResultsManager } from "./analyses-results-manager";
|
|||||||
import { AnalysisResults } from "./shared/analysis-result";
|
import { AnalysisResults } from "./shared/analysis-result";
|
||||||
import { humanizeUnit } from "../pure/time";
|
import { humanizeUnit } from "../pure/time";
|
||||||
import { AbstractWebview, WebviewPanelConfig } from "../abstract-webview";
|
import { AbstractWebview, WebviewPanelConfig } from "../abstract-webview";
|
||||||
|
import { telemetryListener } from "../telemetry";
|
||||||
|
|
||||||
export class RemoteQueriesView extends AbstractWebview<
|
export class RemoteQueriesView extends AbstractWebview<
|
||||||
ToRemoteQueriesMessage,
|
ToRemoteQueriesMessage,
|
||||||
@@ -167,6 +168,9 @@ export class RemoteQueriesView extends AbstractWebview<
|
|||||||
msg.queryId,
|
msg.queryId,
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
|
case "telemetry":
|
||||||
|
telemetryListener?.sendUIInteraction(msg.action);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
assertNever(msg);
|
assertNever(msg);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import {
|
|||||||
VariantAnalysisViewManager,
|
VariantAnalysisViewManager,
|
||||||
} from "./variant-analysis-view-manager";
|
} from "./variant-analysis-view-manager";
|
||||||
import { showAndLogWarningMessage } from "../helpers";
|
import { showAndLogWarningMessage } from "../helpers";
|
||||||
|
import { telemetryListener } from "../telemetry";
|
||||||
|
|
||||||
export class VariantAnalysisView
|
export class VariantAnalysisView
|
||||||
extends AbstractWebview<ToVariantAnalysisMessage, FromVariantAnalysisMessage>
|
extends AbstractWebview<ToVariantAnalysisMessage, FromVariantAnalysisMessage>
|
||||||
@@ -151,6 +152,9 @@ export class VariantAnalysisView
|
|||||||
this.variantAnalysisId,
|
this.variantAnalysisId,
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
|
case "telemetry":
|
||||||
|
telemetryListener?.sendUIInteraction(msg.action);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
assertNever(msg);
|
assertNever(msg);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import {
|
|||||||
GLOBAL_ENABLE_TELEMETRY,
|
GLOBAL_ENABLE_TELEMETRY,
|
||||||
LOG_TELEMETRY,
|
LOG_TELEMETRY,
|
||||||
isIntegrationTestMode,
|
isIntegrationTestMode,
|
||||||
|
isCanary,
|
||||||
} from "./config";
|
} from "./config";
|
||||||
import * as appInsights from "applicationinsights";
|
import * as appInsights from "applicationinsights";
|
||||||
import { extLogger } from "./common";
|
import { extLogger } from "./common";
|
||||||
@@ -155,19 +156,32 @@ export class TelemetryListener extends ConfigListener {
|
|||||||
? CommandCompletion.Cancelled
|
? CommandCompletion.Cancelled
|
||||||
: CommandCompletion.Failed;
|
: CommandCompletion.Failed;
|
||||||
|
|
||||||
const isCanary = (!!CANARY_FEATURES.getValue<boolean>()).toString();
|
|
||||||
|
|
||||||
this.reporter.sendTelemetryEvent(
|
this.reporter.sendTelemetryEvent(
|
||||||
"command-usage",
|
"command-usage",
|
||||||
{
|
{
|
||||||
name,
|
name,
|
||||||
status,
|
status,
|
||||||
isCanary,
|
isCanary: isCanary().toString(),
|
||||||
},
|
},
|
||||||
{ executionTime },
|
{ executionTime },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sendUIInteraction(name: string) {
|
||||||
|
if (!this.reporter) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.reporter.sendTelemetryEvent(
|
||||||
|
"ui-interaction",
|
||||||
|
{
|
||||||
|
name,
|
||||||
|
isCanary: isCanary().toString(),
|
||||||
|
},
|
||||||
|
{},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Displays a popup asking the user if they want to enable telemetry
|
* Displays a popup asking the user if they want to enable telemetry
|
||||||
* for this extension.
|
* for this extension.
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import {
|
|||||||
ResultSeverity,
|
ResultSeverity,
|
||||||
} from "../../../remote-queries/shared/analysis-result";
|
} from "../../../remote-queries/shared/analysis-result";
|
||||||
import { CodePathsOverlay } from "./CodePathsOverlay";
|
import { CodePathsOverlay } from "./CodePathsOverlay";
|
||||||
|
import { useTelemetryOnChange } from "../telemetry";
|
||||||
|
|
||||||
const ShowPathsLink = styled(VSCodeLink)`
|
const ShowPathsLink = styled(VSCodeLink)`
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
@@ -23,6 +24,8 @@ export type CodePathsProps = {
|
|||||||
severity: ResultSeverity;
|
severity: ResultSeverity;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const filterIsOpenTelemetry = (v: boolean) => v;
|
||||||
|
|
||||||
export const CodePaths = ({
|
export const CodePaths = ({
|
||||||
codeFlows,
|
codeFlows,
|
||||||
ruleDescription,
|
ruleDescription,
|
||||||
@@ -30,6 +33,9 @@ export const CodePaths = ({
|
|||||||
severity,
|
severity,
|
||||||
}: CodePathsProps) => {
|
}: CodePathsProps) => {
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
|
useTelemetryOnChange(isOpen, "code-path-is-open", {
|
||||||
|
filterTelemetryOnValue: filterIsOpenTelemetry,
|
||||||
|
});
|
||||||
|
|
||||||
const linkRef = useRef<HTMLAnchorElement>(null);
|
const linkRef = useRef<HTMLAnchorElement>(null);
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import {
|
|||||||
CodeFlow,
|
CodeFlow,
|
||||||
ResultSeverity,
|
ResultSeverity,
|
||||||
} from "../../../remote-queries/shared/analysis-result";
|
} from "../../../remote-queries/shared/analysis-result";
|
||||||
|
import { useTelemetryOnChange } from "../telemetry";
|
||||||
import { SectionTitle } from "../SectionTitle";
|
import { SectionTitle } from "../SectionTitle";
|
||||||
import { VerticalSpace } from "../VerticalSpace";
|
import { VerticalSpace } from "../VerticalSpace";
|
||||||
import { CodeFlowsDropdown } from "./CodeFlowsDropdown";
|
import { CodeFlowsDropdown } from "./CodeFlowsDropdown";
|
||||||
@@ -77,6 +78,7 @@ export const CodePathsOverlay = ({
|
|||||||
onClose,
|
onClose,
|
||||||
}: CodePathsOverlayProps) => {
|
}: CodePathsOverlayProps) => {
|
||||||
const [selectedCodeFlow, setSelectedCodeFlow] = useState(codeFlows[0]);
|
const [selectedCodeFlow, setSelectedCodeFlow] = useState(codeFlows[0]);
|
||||||
|
useTelemetryOnChange(selectedCodeFlow, "code-flow-selected");
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<OverlayContainer>
|
<OverlayContainer>
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import {
|
|||||||
} from "../../../remote-queries/shared/analysis-result";
|
} from "../../../remote-queries/shared/analysis-result";
|
||||||
import { createRemoteFileRef } from "../../../pure/location-link-utils";
|
import { createRemoteFileRef } from "../../../pure/location-link-utils";
|
||||||
import { VerticalSpace } from "../VerticalSpace";
|
import { VerticalSpace } from "../VerticalSpace";
|
||||||
|
import { sendTelemetry } from "../telemetry";
|
||||||
|
|
||||||
const getSeverityColor = (severity: ResultSeverity) => {
|
const getSeverityColor = (severity: ResultSeverity) => {
|
||||||
switch (severity) {
|
switch (severity) {
|
||||||
@@ -49,6 +50,8 @@ type CodeSnippetMessageProps = {
|
|||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const sendAlertMessageLinkTelemetry = () => sendTelemetry("alert-message-link");
|
||||||
|
|
||||||
export const CodeSnippetMessage = ({
|
export const CodeSnippetMessage = ({
|
||||||
message,
|
message,
|
||||||
severity,
|
severity,
|
||||||
@@ -65,6 +68,7 @@ export const CodeSnippetMessage = ({
|
|||||||
return (
|
return (
|
||||||
<LocationLink
|
<LocationLink
|
||||||
key={index}
|
key={index}
|
||||||
|
onClick={sendAlertMessageLinkTelemetry}
|
||||||
href={createRemoteFileRef(
|
href={createRemoteFileRef(
|
||||||
token.location.fileLink,
|
token.location.fileLink,
|
||||||
token.location.highlightedRegion?.startLine,
|
token.location.highlightedRegion?.startLine,
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import {
|
|||||||
import { createRemoteFileRef } from "../../../pure/location-link-utils";
|
import { createRemoteFileRef } from "../../../pure/location-link-utils";
|
||||||
import { CodeSnippetMessage } from "./CodeSnippetMessage";
|
import { CodeSnippetMessage } from "./CodeSnippetMessage";
|
||||||
import { CodeSnippetLine } from "./CodeSnippetLine";
|
import { CodeSnippetLine } from "./CodeSnippetLine";
|
||||||
|
import { sendTelemetry } from "../telemetry";
|
||||||
|
|
||||||
const borderColor = "var(--vscode-editor-snippetFinalTabstopHighlightBorder)";
|
const borderColor = "var(--vscode-editor-snippetFinalTabstopHighlightBorder)";
|
||||||
|
|
||||||
@@ -46,6 +47,9 @@ type Props = {
|
|||||||
messageChildren?: React.ReactNode;
|
messageChildren?: React.ReactNode;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const sendCodeSnippetTitleLinkTelemetry = () =>
|
||||||
|
sendTelemetry("file-code-snippet-title-link");
|
||||||
|
|
||||||
export const FileCodeSnippet = ({
|
export const FileCodeSnippet = ({
|
||||||
fileLink,
|
fileLink,
|
||||||
codeSnippet,
|
codeSnippet,
|
||||||
@@ -67,7 +71,12 @@ export const FileCodeSnippet = ({
|
|||||||
return (
|
return (
|
||||||
<Container>
|
<Container>
|
||||||
<TitleContainer>
|
<TitleContainer>
|
||||||
<VSCodeLink href={titleFileUri}>{fileLink.filePath}</VSCodeLink>
|
<VSCodeLink
|
||||||
|
onClick={sendCodeSnippetTitleLinkTelemetry}
|
||||||
|
href={titleFileUri}
|
||||||
|
>
|
||||||
|
{fileLink.filePath}
|
||||||
|
</VSCodeLink>
|
||||||
</TitleContainer>
|
</TitleContainer>
|
||||||
{message && severity && (
|
{message && severity && (
|
||||||
<CodeSnippetMessage message={message} severity={severity}>
|
<CodeSnippetMessage message={message} severity={severity}>
|
||||||
@@ -83,7 +92,12 @@ export const FileCodeSnippet = ({
|
|||||||
return (
|
return (
|
||||||
<Container>
|
<Container>
|
||||||
<TitleContainer>
|
<TitleContainer>
|
||||||
<VSCodeLink href={titleFileUri}>{fileLink.filePath}</VSCodeLink>
|
<VSCodeLink
|
||||||
|
onClick={sendCodeSnippetTitleLinkTelemetry}
|
||||||
|
href={titleFileUri}
|
||||||
|
>
|
||||||
|
{fileLink.filePath}
|
||||||
|
</VSCodeLink>
|
||||||
</TitleContainer>
|
</TitleContainer>
|
||||||
<CodeContainer>
|
<CodeContainer>
|
||||||
{code.map((line, index) => (
|
{code.map((line, index) => (
|
||||||
|
|||||||
61
extensions/ql-vscode/src/view/common/telemetry.ts
Normal file
61
extensions/ql-vscode/src/view/common/telemetry.ts
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
import { useEffect, useMemo, useRef } from "react";
|
||||||
|
import { vscode } from "../vscode-api";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A react effect that outputs telemetry events whenever the value changes.
|
||||||
|
*
|
||||||
|
* @param value Default value to pass to React.useState
|
||||||
|
* @param telemetryAction Name of the telemetry event to output
|
||||||
|
* @param options Extra optional arguments, including:
|
||||||
|
* filterTelemetryOnValue: If provided, only output telemetry events when the
|
||||||
|
* predicate returns true. If not provided always outputs telemetry.
|
||||||
|
* debounceTimeout: If provided, will not output telemetry events for every change
|
||||||
|
* but will wait until specified timeout happens with no new events ocurring.
|
||||||
|
*/
|
||||||
|
export function useTelemetryOnChange<S>(
|
||||||
|
value: S,
|
||||||
|
telemetryAction: string,
|
||||||
|
{
|
||||||
|
filterTelemetryOnValue,
|
||||||
|
debounceTimeoutMillis,
|
||||||
|
}: {
|
||||||
|
filterTelemetryOnValue?: (value: S) => boolean;
|
||||||
|
debounceTimeoutMillis?: number;
|
||||||
|
} = {},
|
||||||
|
) {
|
||||||
|
const previousValue = useRef(value);
|
||||||
|
|
||||||
|
const sendTelemetryFunc = useMemo<() => void>(() => {
|
||||||
|
if (debounceTimeoutMillis === undefined) {
|
||||||
|
return () => sendTelemetry(telemetryAction);
|
||||||
|
} else {
|
||||||
|
let timer: NodeJS.Timeout;
|
||||||
|
return () => {
|
||||||
|
clearTimeout(timer);
|
||||||
|
timer = setTimeout(() => {
|
||||||
|
sendTelemetry(telemetryAction);
|
||||||
|
}, debounceTimeoutMillis);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}, [telemetryAction, debounceTimeoutMillis]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (value === previousValue.current) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
previousValue.current = value;
|
||||||
|
|
||||||
|
if (filterTelemetryOnValue && !filterTelemetryOnValue(value)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
sendTelemetryFunc();
|
||||||
|
}, [sendTelemetryFunc, filterTelemetryOnValue, value, previousValue]);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function sendTelemetry(telemetryAction: string) {
|
||||||
|
vscode.postMessage({
|
||||||
|
t: "telemetry",
|
||||||
|
action: telemetryAction,
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -10,6 +10,7 @@ import {
|
|||||||
import { tryGetRemoteLocation } from "../../pure/bqrs-utils";
|
import { tryGetRemoteLocation } from "../../pure/bqrs-utils";
|
||||||
import TextButton from "./TextButton";
|
import TextButton from "./TextButton";
|
||||||
import { convertNonPrintableChars } from "../../text-utils";
|
import { convertNonPrintableChars } from "../../text-utils";
|
||||||
|
import { sendTelemetry, useTelemetryOnChange } from "../common/telemetry";
|
||||||
|
|
||||||
const numOfResultsInContractedMode = 5;
|
const numOfResultsInContractedMode = 5;
|
||||||
|
|
||||||
@@ -45,6 +46,8 @@ type CellProps = {
|
|||||||
sourceLocationPrefix: string;
|
sourceLocationPrefix: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const sendRawResultsLinkTelemetry = () => sendTelemetry("raw-results-link");
|
||||||
|
|
||||||
const Cell = ({ value, fileLinkPrefix, sourceLocationPrefix }: CellProps) => {
|
const Cell = ({ value, fileLinkPrefix, sourceLocationPrefix }: CellProps) => {
|
||||||
switch (typeof value) {
|
switch (typeof value) {
|
||||||
case "string":
|
case "string":
|
||||||
@@ -59,7 +62,11 @@ const Cell = ({ value, fileLinkPrefix, sourceLocationPrefix }: CellProps) => {
|
|||||||
);
|
);
|
||||||
const safeLabel = convertNonPrintableChars(value.label);
|
const safeLabel = convertNonPrintableChars(value.label);
|
||||||
if (url) {
|
if (url) {
|
||||||
return <VSCodeLink href={url}>{safeLabel}</VSCodeLink>;
|
return (
|
||||||
|
<VSCodeLink onClick={sendRawResultsLinkTelemetry} href={url}>
|
||||||
|
{safeLabel}
|
||||||
|
</VSCodeLink>
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
return <span>{safeLabel}</span>;
|
return <span>{safeLabel}</span>;
|
||||||
}
|
}
|
||||||
@@ -94,6 +101,8 @@ type RawResultsTableProps = {
|
|||||||
sourceLocationPrefix: string;
|
sourceLocationPrefix: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const filterTableExpandedTelemetry = (v: boolean) => v;
|
||||||
|
|
||||||
const RawResultsTable = ({
|
const RawResultsTable = ({
|
||||||
schema,
|
schema,
|
||||||
results,
|
results,
|
||||||
@@ -101,6 +110,9 @@ const RawResultsTable = ({
|
|||||||
sourceLocationPrefix,
|
sourceLocationPrefix,
|
||||||
}: RawResultsTableProps) => {
|
}: RawResultsTableProps) => {
|
||||||
const [tableExpanded, setTableExpanded] = useState(false);
|
const [tableExpanded, setTableExpanded] = useState(false);
|
||||||
|
useTelemetryOnChange(tableExpanded, "raw-results-table-expanded", {
|
||||||
|
filterTelemetryOnValue: filterTableExpandedTelemetry,
|
||||||
|
});
|
||||||
const numOfResultsToShow = tableExpanded
|
const numOfResultsToShow = tableExpanded
|
||||||
? results.rows.length
|
? results.rows.length
|
||||||
: numOfResultsInContractedMode;
|
: numOfResultsInContractedMode;
|
||||||
|
|||||||
@@ -433,7 +433,7 @@ const AnalysesResults = ({
|
|||||||
sort: Sort;
|
sort: Sort;
|
||||||
}) => {
|
}) => {
|
||||||
const totalAnalysesResults = sumAnalysesResults(analysesResults);
|
const totalAnalysesResults = sumAnalysesResults(analysesResults);
|
||||||
const [filterValue, setFilterValue] = React.useState("");
|
const [filterValue, setFilterValue] = useState("");
|
||||||
|
|
||||||
if (totalResults === 0) {
|
if (totalResults === 0) {
|
||||||
return <></>;
|
return <></>;
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import { vscode } from "../vscode-api";
|
|||||||
import { AnalyzedRepoItemContent } from "./AnalyzedRepoItemContent";
|
import { AnalyzedRepoItemContent } from "./AnalyzedRepoItemContent";
|
||||||
import StarCount from "../common/StarCount";
|
import StarCount from "../common/StarCount";
|
||||||
import { LastUpdated } from "../common/LastUpdated";
|
import { LastUpdated } from "../common/LastUpdated";
|
||||||
|
import { useTelemetryOnChange } from "../common/telemetry";
|
||||||
|
|
||||||
// This will ensure that these icons have a className which we can use in the TitleContainer
|
// This will ensure that these icons have a className which we can use in the TitleContainer
|
||||||
const ExpandCollapseCodicon = styled(Codicon)``;
|
const ExpandCollapseCodicon = styled(Codicon)``;
|
||||||
@@ -157,6 +158,8 @@ const isExpandableContentLoaded = (
|
|||||||
return resultsLoaded;
|
return resultsLoaded;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const filterRepoRowExpandedTelemetry = (v: boolean) => v;
|
||||||
|
|
||||||
export const RepoRow = ({
|
export const RepoRow = ({
|
||||||
repository,
|
repository,
|
||||||
status,
|
status,
|
||||||
@@ -168,6 +171,9 @@ export const RepoRow = ({
|
|||||||
onSelectedChange,
|
onSelectedChange,
|
||||||
}: RepoRowProps) => {
|
}: RepoRowProps) => {
|
||||||
const [isExpanded, setExpanded] = useState(false);
|
const [isExpanded, setExpanded] = useState(false);
|
||||||
|
useTelemetryOnChange(isExpanded, "variant-analysis-repo-row-expanded", {
|
||||||
|
filterTelemetryOnValue: filterRepoRowExpandedTelemetry,
|
||||||
|
});
|
||||||
const resultsLoaded = !!interpretedResults || !!rawResults;
|
const resultsLoaded = !!interpretedResults || !!rawResults;
|
||||||
const [resultsLoading, setResultsLoading] = useState(false);
|
const [resultsLoading, setResultsLoading] = useState(false);
|
||||||
|
|
||||||
@@ -198,6 +204,7 @@ export const RepoRow = ({
|
|||||||
repository.fullName,
|
repository.fullName,
|
||||||
status,
|
status,
|
||||||
downloadStatus,
|
downloadStatus,
|
||||||
|
setExpanded,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -205,7 +212,7 @@ export const RepoRow = ({
|
|||||||
setResultsLoading(false);
|
setResultsLoading(false);
|
||||||
setExpanded(true);
|
setExpanded(true);
|
||||||
}
|
}
|
||||||
}, [resultsLoaded, resultsLoading]);
|
}, [resultsLoaded, resultsLoading, setExpanded]);
|
||||||
|
|
||||||
const onClickCheckbox = useCallback((e: React.MouseEvent) => {
|
const onClickCheckbox = useCallback((e: React.MouseEvent) => {
|
||||||
// Prevent calling the onClick event of the container, which would toggle the expanded state
|
// Prevent calling the onClick event of the container, which would toggle the expanded state
|
||||||
|
|||||||
@@ -12,10 +12,8 @@ import { VariantAnalysisOutcomePanels } from "./VariantAnalysisOutcomePanels";
|
|||||||
import { VariantAnalysisLoading } from "./VariantAnalysisLoading";
|
import { VariantAnalysisLoading } from "./VariantAnalysisLoading";
|
||||||
import { ToVariantAnalysisMessage } from "../../pure/interface-types";
|
import { ToVariantAnalysisMessage } from "../../pure/interface-types";
|
||||||
import { vscode } from "../vscode-api";
|
import { vscode } from "../vscode-api";
|
||||||
import {
|
import { defaultFilterSortState } from "../../pure/variant-analysis-filter-sort";
|
||||||
defaultFilterSortState,
|
import { useTelemetryOnChange } from "../common/telemetry";
|
||||||
RepositoriesFilterSortState,
|
|
||||||
} from "../../pure/variant-analysis-filter-sort";
|
|
||||||
|
|
||||||
export type VariantAnalysisProps = {
|
export type VariantAnalysisProps = {
|
||||||
variantAnalysis?: VariantAnalysisDomainModel;
|
variantAnalysis?: VariantAnalysisDomainModel;
|
||||||
@@ -63,8 +61,19 @@ export function VariantAnalysis({
|
|||||||
const [selectedRepositoryIds, setSelectedRepositoryIds] = useState<number[]>(
|
const [selectedRepositoryIds, setSelectedRepositoryIds] = useState<number[]>(
|
||||||
[],
|
[],
|
||||||
);
|
);
|
||||||
const [filterSortState, setFilterSortState] =
|
useTelemetryOnChange(
|
||||||
useState<RepositoriesFilterSortState>(defaultFilterSortState);
|
selectedRepositoryIds,
|
||||||
|
"variant-analysis-selected-repository-ids",
|
||||||
|
{
|
||||||
|
debounceTimeoutMillis: 1000,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
const [filterSortState, setFilterSortState] = useState(
|
||||||
|
defaultFilterSortState,
|
||||||
|
);
|
||||||
|
useTelemetryOnChange(filterSortState, "variant-analysis-filter-sort-state", {
|
||||||
|
debounceTimeoutMillis: 1000,
|
||||||
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const listener = (evt: MessageEvent) => {
|
const listener = (evt: MessageEvent) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user