Skip to content

Commit f325eeb

Browse files
Output telemetry on react state changes in MRVA results view
1 parent bdfb2f2 commit f325eeb

7 files changed

Lines changed: 90 additions & 13 deletions

File tree

extensions/ql-vscode/src/view/common/CodePaths/CodePaths.tsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import * as React from "react";
2-
import { useRef, useState } from "react";
2+
import { useRef } from "react";
33
import styled from "styled-components";
44
import { VSCodeLink } from "@vscode/webview-ui-toolkit/react";
55

@@ -11,6 +11,7 @@ import {
1111
ResultSeverity,
1212
} from "../../../remote-queries/shared/analysis-result";
1313
import { CodePathsOverlay } from "./CodePathsOverlay";
14+
import { useStateWithTelemetry } from "../Telemetry";
1415

1516
const ShowPathsLink = styled(VSCodeLink)`
1617
cursor: pointer;
@@ -29,7 +30,11 @@ export const CodePaths = ({
2930
message,
3031
severity,
3132
}: CodePathsProps) => {
32-
const [isOpen, setIsOpen] = useState(false);
33+
const [isOpen, setIsOpen] = useStateWithTelemetry(
34+
false,
35+
"code-path-is-open",
36+
(v) => v === true,
37+
);
3338

3439
const linkRef = useRef<HTMLAnchorElement>(null);
3540

extensions/ql-vscode/src/view/common/CodePaths/CodePathsOverlay.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import * as React from "react";
2-
import { useState } from "react";
32
import styled from "styled-components";
43

54
import {
65
AnalysisMessage,
76
CodeFlow,
87
ResultSeverity,
98
} from "../../../remote-queries/shared/analysis-result";
9+
import { useStateWithTelemetry } from "../Telemetry";
1010
import { SectionTitle } from "../SectionTitle";
1111
import { VerticalSpace } from "../VerticalSpace";
1212
import { CodeFlowsDropdown } from "./CodeFlowsDropdown";
@@ -76,7 +76,10 @@ export const CodePathsOverlay = ({
7676
severity,
7777
onClose,
7878
}: CodePathsOverlayProps) => {
79-
const [selectedCodeFlow, setSelectedCodeFlow] = useState(codeFlows[0]);
79+
const [selectedCodeFlow, setSelectedCodeFlow] = useStateWithTelemetry(
80+
codeFlows[0],
81+
"code-flow-selected",
82+
);
8083

8184
return (
8285
<OverlayContainer>
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import * as React from "react";
2+
import { useState } from "react";
3+
import { vscode } from "../vscode-api";
4+
5+
/**
6+
* Wraps `React.useState` to output telemetry events whenever the value changes.
7+
*
8+
* The only catch is that when using a predicate to filter which values output telemetry,
9+
* the setter only accepts a raw value, instead of a `(prevState: S) => S` function.
10+
*
11+
* @param defaultValue Default value to pass to React.useState
12+
* @param telemetryAction Name of the telemetry event to output
13+
* @param filterTelemetryOnValue If provided, only output telemetry events when the predicate returns true. If not provided always outputs telemetry.
14+
* @returns A value and a setter function, just as if from `React.useState`
15+
*/
16+
export function useStateWithTelemetry<S>(
17+
defaultValue: S | (() => S),
18+
telemetryAction: string,
19+
): [S, React.Dispatch<React.SetStateAction<S>>];
20+
export function useStateWithTelemetry<S>(
21+
defaultValue: S | (() => S),
22+
telemetryAction: string,
23+
filterTelemetryOnValue: (value: S) => boolean,
24+
): [S, React.Dispatch<S>];
25+
export function useStateWithTelemetry<S>(
26+
defaultValue: S | (() => S),
27+
telemetryAction: string,
28+
filterTelemetryOnValue?: (value: S) => boolean,
29+
): [S, React.Dispatch<S> | React.Dispatch<React.SetStateAction<S>>] {
30+
const [value, setter] = useState<S>(defaultValue);
31+
if (filterTelemetryOnValue === undefined) {
32+
const setterWithTelemetry = (x: React.SetStateAction<S>) => {
33+
vscode.postMessage({
34+
t: "telemetry",
35+
action: telemetryAction,
36+
});
37+
setter(x);
38+
};
39+
return [value, setterWithTelemetry];
40+
} else {
41+
const setterWithTelemetry = (x: S) => {
42+
if (filterTelemetryOnValue(x)) {
43+
vscode.postMessage({
44+
t: "telemetry",
45+
action: telemetryAction,
46+
});
47+
}
48+
setter(x);
49+
};
50+
return [value, setterWithTelemetry];
51+
}
52+
}

extensions/ql-vscode/src/view/remote-queries/RawResultsTable.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import * as React from "react";
2-
import { useState } from "react";
32
import styled from "styled-components";
43
import { VSCodeLink } from "@vscode/webview-ui-toolkit/react";
54
import {
@@ -10,6 +9,7 @@ import {
109
import { tryGetRemoteLocation } from "../../pure/bqrs-utils";
1110
import TextButton from "./TextButton";
1211
import { convertNonPrintableChars } from "../../text-utils";
12+
import { useStateWithTelemetry } from "../common/Telemetry";
1313

1414
const numOfResultsInContractedMode = 5;
1515

@@ -100,7 +100,11 @@ const RawResultsTable = ({
100100
fileLinkPrefix,
101101
sourceLocationPrefix,
102102
}: RawResultsTableProps) => {
103-
const [tableExpanded, setTableExpanded] = useState(false);
103+
const [tableExpanded, setTableExpanded] = useStateWithTelemetry(
104+
false,
105+
"raw-results-table-expanded",
106+
(v) => v === true,
107+
);
104108
const numOfResultsToShow = tableExpanded
105109
? results.rows.length
106110
: numOfResultsInContractedMode;

extensions/ql-vscode/src/view/remote-queries/RemoteQueries.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -433,7 +433,7 @@ const AnalysesResults = ({
433433
sort: Sort;
434434
}) => {
435435
const totalAnalysesResults = sumAnalysesResults(analysesResults);
436-
const [filterValue, setFilterValue] = React.useState("");
436+
const [filterValue, setFilterValue] = useState("");
437437

438438
if (totalResults === 0) {
439439
return <></>;

extensions/ql-vscode/src/view/variant-analysis/RepoRow.tsx

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import { vscode } from "../vscode-api";
2424
import { AnalyzedRepoItemContent } from "./AnalyzedRepoItemContent";
2525
import StarCount from "../common/StarCount";
2626
import { LastUpdated } from "../common/LastUpdated";
27+
import { useStateWithTelemetry } from "../common/Telemetry";
2728

2829
// This will ensure that these icons have a className which we can use in the TitleContainer
2930
const ExpandCollapseCodicon = styled(Codicon)``;
@@ -167,7 +168,11 @@ export const RepoRow = ({
167168
selected,
168169
onSelectedChange,
169170
}: RepoRowProps) => {
170-
const [isExpanded, setExpanded] = useState(false);
171+
const [isExpanded, setExpanded] = useStateWithTelemetry(
172+
false,
173+
"variant-analysis-repo-row-expanded",
174+
(v) => v === true,
175+
);
171176
const resultsLoaded = !!interpretedResults || !!rawResults;
172177
const [resultsLoading, setResultsLoading] = useState(false);
173178

@@ -182,7 +187,7 @@ export const RepoRow = ({
182187
downloadStatus !==
183188
VariantAnalysisScannedRepositoryDownloadStatus.Succeeded
184189
) {
185-
setExpanded((oldIsExpanded) => !oldIsExpanded);
190+
setExpanded(!isExpanded);
186191
return;
187192
}
188193

@@ -198,6 +203,8 @@ export const RepoRow = ({
198203
repository.fullName,
199204
status,
200205
downloadStatus,
206+
isExpanded,
207+
setExpanded,
201208
]);
202209

203210
useEffect(() => {

extensions/ql-vscode/src/view/variant-analysis/VariantAnalysis.tsx

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import {
1616
defaultFilterSortState,
1717
RepositoriesFilterSortState,
1818
} from "../../pure/variant-analysis-filter-sort";
19+
import { useStateWithTelemetry } from "../common/Telemetry";
1920

2021
export type VariantAnalysisProps = {
2122
variantAnalysis?: VariantAnalysisDomainModel;
@@ -60,11 +61,16 @@ export function VariantAnalysis({
6061
const [repoResults, setRepoResults] =
6162
useState<VariantAnalysisScannedRepositoryResult[]>(initialRepoResults);
6263

63-
const [selectedRepositoryIds, setSelectedRepositoryIds] = useState<number[]>(
64-
[],
65-
);
64+
const [selectedRepositoryIds, setSelectedRepositoryIds] =
65+
useStateWithTelemetry<number[]>(
66+
[],
67+
"variant-analysis-selected-repository-ids",
68+
);
6669
const [filterSortState, setFilterSortState] =
67-
useState<RepositoriesFilterSortState>(defaultFilterSortState);
70+
useStateWithTelemetry<RepositoriesFilterSortState>(
71+
defaultFilterSortState,
72+
"variant-analysis-filter-sort-state",
73+
);
6874

6975
useEffect(() => {
7076
const listener = (evt: MessageEvent) => {

0 commit comments

Comments
 (0)