Convert useStateWithTelemetry to useTelemetryOnChange

This commit is contained in:
Robert
2023-01-13 11:59:23 +00:00
parent e9830ee854
commit 98a29d2459
6 changed files with 61 additions and 81 deletions

View File

@@ -1,5 +1,5 @@
import * as React from "react";
import { useRef } from "react";
import { useRef, useState } from "react";
import styled from "styled-components";
import { VSCodeLink } from "@vscode/webview-ui-toolkit/react";
@@ -11,7 +11,7 @@ import {
ResultSeverity,
} from "../../../remote-queries/shared/analysis-result";
import { CodePathsOverlay } from "./CodePathsOverlay";
import { useStateWithTelemetry } from "../telemetry";
import { useTelemetryOnChange } from "../telemetry";
const ShowPathsLink = styled(VSCodeLink)`
cursor: pointer;
@@ -32,11 +32,10 @@ export const CodePaths = ({
message,
severity,
}: CodePathsProps) => {
const [isOpen, setIsOpen] = useStateWithTelemetry<boolean>(
false,
"code-path-is-open",
filterIsOpenTelemetry,
);
const [isOpen, setIsOpen] = useState(false);
useTelemetryOnChange(isOpen, "code-path-is-open", {
filterTelemetryOnValue: filterIsOpenTelemetry,
});
const linkRef = useRef<HTMLAnchorElement>(null);

View File

@@ -1,4 +1,5 @@
import * as React from "react";
import { useState } from "react";
import styled from "styled-components";
import {
@@ -6,7 +7,7 @@ import {
CodeFlow,
ResultSeverity,
} from "../../../remote-queries/shared/analysis-result";
import { useStateWithTelemetry } from "../telemetry";
import { useTelemetryOnChange } from "../telemetry";
import { SectionTitle } from "../SectionTitle";
import { VerticalSpace } from "../VerticalSpace";
import { CodeFlowsDropdown } from "./CodeFlowsDropdown";
@@ -76,10 +77,8 @@ export const CodePathsOverlay = ({
severity,
onClose,
}: CodePathsOverlayProps) => {
const [selectedCodeFlow, setSelectedCodeFlow] = useStateWithTelemetry(
codeFlows[0],
"code-flow-selected",
);
const [selectedCodeFlow, setSelectedCodeFlow] = useState(codeFlows[0]);
useTelemetryOnChange(selectedCodeFlow, "code-flow-selected");
return (
<OverlayContainer>

View File

@@ -1,51 +1,37 @@
import * as React from "react";
import { useState } from "react";
import { useEffect, useRef } from "react";
import { vscode } from "../vscode-api";
/**
* Wraps `React.useState` to output telemetry events whenever the value changes.
* A react effect that outputs telemetry events whenever the value changes.
*
* The only catch is that when using a predicate to filter which values output telemetry,
* the setter only accepts a raw value, instead of a `(prevState: S) => S` function.
*
* @param defaultValue Default value to pass to React.useState
* @param value Default value to pass to React.useState
* @param telemetryAction Name of the telemetry event to output
* @param filterTelemetryOnValue If provided, only output telemetry events when the predicate returns true. If not provided always outputs telemetry.
* @returns A value and a setter function, just as if from `React.useState`
* @param options Extra optional arguments, including:
* filterTelemetryOnValue: If provided, only output telemetry events when the
* predicate returns true. If not provided always outputs telemetry.
*/
export function useStateWithTelemetry<S>(
defaultValue: S | (() => S),
export function useTelemetryOnChange<S>(
value: S,
telemetryAction: string,
): [S, React.Dispatch<React.SetStateAction<S>>];
export function useStateWithTelemetry<S>(
defaultValue: S | (() => S),
telemetryAction: string,
filterTelemetryOnValue: (value: S) => boolean,
): [S, React.Dispatch<S>];
export function useStateWithTelemetry<S>(
defaultValue: S | (() => S),
telemetryAction: string,
filterTelemetryOnValue?: (value: S) => boolean,
): [S, React.Dispatch<S> | React.Dispatch<React.SetStateAction<S>>] {
const [value, setter] = useState<S>(defaultValue);
const setterWithTelemetry = React.useMemo<
React.Dispatch<S> | React.Dispatch<React.SetStateAction<S>>
>(() => {
if (filterTelemetryOnValue === undefined) {
return (x: React.SetStateAction<S>) => {
sendTelemetry(telemetryAction);
setter(x);
};
} else {
return (x: S) => {
if (filterTelemetryOnValue(x)) {
sendTelemetry(telemetryAction);
}
setter(x);
};
options?: {
filterTelemetryOnValue?: (value: S) => boolean;
},
) {
const previousValue = useRef(value);
const filterTelemetryOnValue = options?.filterTelemetryOnValue;
useEffect(() => {
if (value === previousValue.current) {
return;
}
}, [telemetryAction, filterTelemetryOnValue, setter]);
return [value, setterWithTelemetry];
previousValue.current = value;
if (filterTelemetryOnValue && !filterTelemetryOnValue(value)) {
return;
}
sendTelemetry(telemetryAction);
}, [telemetryAction, filterTelemetryOnValue, value, previousValue]);
}
export function sendTelemetry(telemetryAction: string) {

View File

@@ -1,4 +1,5 @@
import * as React from "react";
import { useState } from "react";
import styled from "styled-components";
import { VSCodeLink } from "@vscode/webview-ui-toolkit/react";
import {
@@ -9,7 +10,7 @@ import {
import { tryGetRemoteLocation } from "../../pure/bqrs-utils";
import TextButton from "./TextButton";
import { convertNonPrintableChars } from "../../text-utils";
import { sendTelemetry, useStateWithTelemetry } from "../common/telemetry";
import { sendTelemetry, useTelemetryOnChange } from "../common/telemetry";
const numOfResultsInContractedMode = 5;
@@ -108,11 +109,10 @@ const RawResultsTable = ({
fileLinkPrefix,
sourceLocationPrefix,
}: RawResultsTableProps) => {
const [tableExpanded, setTableExpanded] = useStateWithTelemetry<boolean>(
false,
"raw-results-table-expanded",
filterTableExpandedTelemetry,
);
const [tableExpanded, setTableExpanded] = useState(false);
useTelemetryOnChange(tableExpanded, "raw-results-table-expanded", {
filterTelemetryOnValue: filterTableExpandedTelemetry,
});
const numOfResultsToShow = tableExpanded
? results.rows.length
: numOfResultsInContractedMode;

View File

@@ -24,7 +24,7 @@ import { vscode } from "../vscode-api";
import { AnalyzedRepoItemContent } from "./AnalyzedRepoItemContent";
import StarCount from "../common/StarCount";
import { LastUpdated } from "../common/LastUpdated";
import { useStateWithTelemetry } from "../common/telemetry";
import { useTelemetryOnChange } from "../common/telemetry";
// This will ensure that these icons have a className which we can use in the TitleContainer
const ExpandCollapseCodicon = styled(Codicon)``;
@@ -170,11 +170,10 @@ export const RepoRow = ({
selected,
onSelectedChange,
}: RepoRowProps) => {
const [isExpanded, setExpanded] = useStateWithTelemetry<boolean>(
false,
"variant-analysis-repo-row-expanded",
filterRepoRowExpandedTelemetry,
);
const [isExpanded, setExpanded] = useState(false);
useTelemetryOnChange(isExpanded, "variant-analysis-repo-row-expanded", {
filterTelemetryOnValue: filterRepoRowExpandedTelemetry,
});
const resultsLoaded = !!interpretedResults || !!rawResults;
const [resultsLoading, setResultsLoading] = useState(false);
@@ -189,7 +188,7 @@ export const RepoRow = ({
downloadStatus !==
VariantAnalysisScannedRepositoryDownloadStatus.Succeeded
) {
setExpanded(!isExpanded);
setExpanded((oldIsExpanded) => !oldIsExpanded);
return;
}
@@ -205,7 +204,6 @@ export const RepoRow = ({
repository.fullName,
status,
downloadStatus,
isExpanded,
setExpanded,
]);

View File

@@ -12,11 +12,8 @@ import { VariantAnalysisOutcomePanels } from "./VariantAnalysisOutcomePanels";
import { VariantAnalysisLoading } from "./VariantAnalysisLoading";
import { ToVariantAnalysisMessage } from "../../pure/interface-types";
import { vscode } from "../vscode-api";
import {
defaultFilterSortState,
RepositoriesFilterSortState,
} from "../../pure/variant-analysis-filter-sort";
import { useStateWithTelemetry } from "../common/telemetry";
import { defaultFilterSortState } from "../../pure/variant-analysis-filter-sort";
import { useTelemetryOnChange } from "../common/telemetry";
export type VariantAnalysisProps = {
variantAnalysis?: VariantAnalysisDomainModel;
@@ -61,16 +58,17 @@ export function VariantAnalysis({
const [repoResults, setRepoResults] =
useState<VariantAnalysisScannedRepositoryResult[]>(initialRepoResults);
const [selectedRepositoryIds, setSelectedRepositoryIds] =
useStateWithTelemetry<number[]>(
[],
"variant-analysis-selected-repository-ids",
);
const [filterSortState, setFilterSortState] =
useStateWithTelemetry<RepositoriesFilterSortState>(
defaultFilterSortState,
"variant-analysis-filter-sort-state",
);
const [selectedRepositoryIds, setSelectedRepositoryIds] = useState<number[]>(
[],
);
useTelemetryOnChange(
selectedRepositoryIds,
"variant-analysis-selected-repository-ids",
);
const [filterSortState, setFilterSortState] = useState(
defaultFilterSortState,
);
useTelemetryOnChange(filterSortState, "variant-analysis-filter-sort-state");
useEffect(() => {
const listener = (evt: MessageEvent) => {