Skip to content

Commit ea45e38

Browse files
authored
Merge pull request #3802 from asgerf/asgerf/use-message-from-extension
Add useMessageFromExtension hook
2 parents 873d4e3 + bd38355 commit ea45e38

File tree

8 files changed

+233
-309
lines changed

8 files changed

+233
-309
lines changed
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { useEffect } from "react";
2+
3+
/**
4+
* Invokes the given callback when a message is received from the extension.
5+
*/
6+
export function useMessageFromExtension<T>(
7+
onEvent: (event: T) => void,
8+
onEventDependencies: unknown[],
9+
): void {
10+
useEffect(() => {
11+
const listener = (evt: MessageEvent) => {
12+
if (evt.origin === window.origin) {
13+
onEvent(evt.data as T);
14+
} else {
15+
// sanitize origin
16+
const origin = evt.origin.replace(/\n|\r/g, "");
17+
console.error(`Invalid event origin ${origin}`);
18+
}
19+
};
20+
window.addEventListener("message", listener);
21+
22+
return () => {
23+
window.removeEventListener("message", listener);
24+
};
25+
// eslint-disable-next-line react-hooks/exhaustive-deps
26+
}, onEventDependencies);
27+
}

extensions/ql-vscode/src/view/compare/Compare.tsx

Lines changed: 83 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useState, useEffect, useRef } from "react";
1+
import { useState, useRef } from "react";
22
import { styled } from "styled-components";
33

44
import type {
@@ -16,6 +16,7 @@ import CompareTable from "./CompareTable";
1616

1717
import "../results/resultsView.css";
1818
import { assertNever } from "../../common/helpers-pure";
19+
import { useMessageFromExtension } from "../common/useMessageFromExtension";
1920

2021
const Header = styled.div`
2122
display: flex;
@@ -50,115 +51,101 @@ export function Compare(_: Record<string, never>): React.JSX.Element {
5051
comparison?.result &&
5152
(comparison.result.to.length || comparison.result.from.length);
5253

53-
useEffect(() => {
54-
const listener = (evt: MessageEvent) => {
55-
if (evt.origin === window.origin) {
56-
const msg: ToCompareViewMessage = evt.data;
57-
switch (msg.t) {
58-
case "setComparisonQueryInfo":
59-
setQueryInfo(msg);
60-
break;
61-
case "setComparisons":
62-
setComparison(msg);
63-
break;
64-
case "streamingComparisonSetup":
65-
setComparison(null);
66-
streamingComparisonRef.current = msg;
67-
break;
68-
case "streamingComparisonAddResults": {
69-
const prev = streamingComparisonRef.current;
70-
if (prev === null) {
71-
console.warn(
72-
'Received "streamingComparisonAddResults" before "streamingComparisonSetup"',
73-
);
74-
break;
75-
}
54+
useMessageFromExtension<ToCompareViewMessage>((msg) => {
55+
switch (msg.t) {
56+
case "setComparisonQueryInfo":
57+
setQueryInfo(msg);
58+
break;
59+
case "setComparisons":
60+
setComparison(msg);
61+
break;
62+
case "streamingComparisonSetup":
63+
setComparison(null);
64+
streamingComparisonRef.current = msg;
65+
break;
66+
case "streamingComparisonAddResults": {
67+
const prev = streamingComparisonRef.current;
68+
if (prev === null) {
69+
console.warn(
70+
'Received "streamingComparisonAddResults" before "streamingComparisonSetup"',
71+
);
72+
break;
73+
}
7674

77-
if (prev.id !== msg.id) {
78-
console.warn(
79-
'Received "streamingComparisonAddResults" with different id, ignoring',
80-
);
81-
break;
82-
}
75+
if (prev.id !== msg.id) {
76+
console.warn(
77+
'Received "streamingComparisonAddResults" with different id, ignoring',
78+
);
79+
break;
80+
}
8381

84-
let result: QueryCompareResult;
85-
switch (prev.result.kind) {
86-
case "raw":
87-
if (msg.result.kind !== "raw") {
88-
throw new Error(
89-
"Streaming comparison: expected raw results, got interpreted results",
90-
);
91-
}
92-
93-
result = {
94-
...prev.result,
95-
from: [...prev.result.from, ...msg.result.from],
96-
to: [...prev.result.to, ...msg.result.to],
97-
};
98-
break;
99-
case "interpreted":
100-
if (msg.result.kind !== "interpreted") {
101-
throw new Error(
102-
"Streaming comparison: expected interpreted results, got raw results",
103-
);
104-
}
105-
106-
result = {
107-
...prev.result,
108-
from: [...prev.result.from, ...msg.result.from],
109-
to: [...prev.result.to, ...msg.result.to],
110-
};
111-
break;
112-
default:
113-
throw new Error("Unexpected comparison result kind");
82+
let result: QueryCompareResult;
83+
switch (prev.result.kind) {
84+
case "raw":
85+
if (msg.result.kind !== "raw") {
86+
throw new Error(
87+
"Streaming comparison: expected raw results, got interpreted results",
88+
);
11489
}
11590

116-
streamingComparisonRef.current = {
117-
...prev,
118-
result,
91+
result = {
92+
...prev.result,
93+
from: [...prev.result.from, ...msg.result.from],
94+
to: [...prev.result.to, ...msg.result.to],
11995
};
120-
12196
break;
122-
}
123-
case "streamingComparisonComplete":
124-
if (streamingComparisonRef.current === null) {
125-
console.warn(
126-
'Received "streamingComparisonComplete" before "streamingComparisonSetup"',
97+
case "interpreted":
98+
if (msg.result.kind !== "interpreted") {
99+
throw new Error(
100+
"Streaming comparison: expected interpreted results, got raw results",
127101
);
128-
setComparison(null);
129-
break;
130102
}
131103

132-
if (streamingComparisonRef.current.id !== msg.id) {
133-
console.warn(
134-
'Received "streamingComparisonComplete" with different id, ignoring',
135-
);
136-
break;
137-
}
138-
139-
setComparison({
140-
...streamingComparisonRef.current,
141-
t: "setComparisons",
142-
});
143-
streamingComparisonRef.current = null;
144-
break;
145-
case "setUserSettings":
146-
setUserSettings(msg.userSettings);
104+
result = {
105+
...prev.result,
106+
from: [...prev.result.from, ...msg.result.from],
107+
to: [...prev.result.to, ...msg.result.to],
108+
};
147109
break;
148110
default:
149-
assertNever(msg);
111+
throw new Error("Unexpected comparison result kind");
150112
}
151-
} else {
152-
// sanitize origin
153-
const origin = evt.origin.replace(/\n|\r/g, "");
154-
console.error(`Invalid event origin ${origin}`);
113+
114+
streamingComparisonRef.current = {
115+
...prev,
116+
result,
117+
};
118+
119+
break;
155120
}
156-
};
157-
window.addEventListener("message", listener);
121+
case "streamingComparisonComplete":
122+
if (streamingComparisonRef.current === null) {
123+
console.warn(
124+
'Received "streamingComparisonComplete" before "streamingComparisonSetup"',
125+
);
126+
setComparison(null);
127+
break;
128+
}
129+
130+
if (streamingComparisonRef.current.id !== msg.id) {
131+
console.warn(
132+
'Received "streamingComparisonComplete" with different id, ignoring',
133+
);
134+
break;
135+
}
158136

159-
return () => {
160-
window.removeEventListener("message", listener);
161-
};
137+
setComparison({
138+
...streamingComparisonRef.current,
139+
t: "setComparisons",
140+
});
141+
streamingComparisonRef.current = null;
142+
break;
143+
case "setUserSettings":
144+
setUserSettings(msg.userSettings);
145+
break;
146+
default:
147+
assertNever(msg);
148+
}
162149
}, []);
163150

164151
if (!queryInfo || !comparison) {

extensions/ql-vscode/src/view/data-flow-paths/DataFlowPathsView.tsx

Lines changed: 7 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
import { useEffect, useState } from "react";
1+
import { useState } from "react";
22
import type { ToDataFlowPathsMessage } from "../../common/interface-types";
33
import type { DataFlowPaths as DataFlowPathsDomainModel } from "../../variant-analysis/shared/data-flow-paths";
44
import { DataFlowPaths } from "./DataFlowPaths";
5+
import { useMessageFromExtension } from "../common/useMessageFromExtension";
56

67
export type DataFlowPathsViewProps = {
78
dataFlowPaths?: DataFlowPathsDomainModel;
@@ -14,28 +15,12 @@ export function DataFlowPathsView({
1415
DataFlowPathsDomainModel | undefined
1516
>(initialDataFlowPaths);
1617

17-
useEffect(() => {
18-
const listener = (evt: MessageEvent) => {
19-
if (evt.origin === window.origin) {
20-
const msg: ToDataFlowPathsMessage = evt.data;
21-
if (msg.t === "setDataFlowPaths") {
22-
setDataFlowPaths(msg.dataFlowPaths);
18+
useMessageFromExtension<ToDataFlowPathsMessage>((msg) => {
19+
setDataFlowPaths(msg.dataFlowPaths);
2320

24-
// Scroll to the top of the page when we're rendering
25-
// new data flow paths.
26-
window.scrollTo(0, 0);
27-
}
28-
} else {
29-
// sanitize origin
30-
const origin = evt.origin.replace(/\n|\r/g, "");
31-
console.error(`Invalid event origin ${origin}`);
32-
}
33-
};
34-
window.addEventListener("message", listener);
35-
36-
return () => {
37-
window.removeEventListener("message", listener);
38-
};
21+
// Scroll to the top of the page when we're rendering
22+
// new data flow paths.
23+
window.scrollTo(0, 0);
3924
}, []);
4025

4126
if (!dataFlowPaths) {

extensions/ql-vscode/src/view/method-modeling/MethodModelingView.tsx

Lines changed: 29 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useEffect, useMemo, useState } from "react";
1+
import { useMemo, useState } from "react";
22
import { MethodModeling } from "./MethodModeling";
33
import { getModelingStatus } from "../../model-editor/shared/modeling-status";
44
import type { Method } from "../../model-editor/method";
@@ -12,6 +12,7 @@ import { NoMethodSelected } from "./NoMethodSelected";
1212
import type { MethodModelingPanelViewState } from "../../model-editor/shared/view-state";
1313
import { MethodAlreadyModeled } from "./MethodAlreadyModeled";
1414
import { defaultModelConfig } from "../../model-editor/languages";
15+
import { useMessageFromExtension } from "../common/useMessageFromExtension";
1516

1617
type Props = {
1718
initialViewState?: MethodModelingPanelViewState;
@@ -36,47 +37,33 @@ export function MethodModelingView({
3637
[modeledMethods, isMethodModified],
3738
);
3839

39-
useEffect(() => {
40-
const listener = (evt: MessageEvent) => {
41-
if (evt.origin === window.origin) {
42-
const msg: ToMethodModelingMessage = evt.data;
43-
switch (msg.t) {
44-
case "setMethodModelingPanelViewState":
45-
setViewState(msg.viewState);
46-
break;
47-
case "setInModelingMode":
48-
setInModelingMode(msg.inModelingMode);
49-
break;
50-
case "setMultipleModeledMethods":
51-
setModeledMethods(msg.modeledMethods);
52-
break;
53-
case "setMethodModified":
54-
setIsMethodModified(msg.isModified);
55-
break;
56-
case "setNoMethodSelected":
57-
setMethod(undefined);
58-
setModeledMethods([]);
59-
setIsMethodModified(false);
60-
break;
61-
case "setSelectedMethod":
62-
setMethod(msg.method);
63-
setModeledMethods(msg.modeledMethods);
64-
setIsMethodModified(msg.isModified);
65-
break;
66-
default:
67-
assertNever(msg);
68-
}
69-
} else {
70-
// sanitize origin
71-
const origin = evt.origin.replace(/\n|\r/g, "");
72-
console.error(`Invalid event origin ${origin}`);
73-
}
74-
};
75-
window.addEventListener("message", listener);
76-
77-
return () => {
78-
window.removeEventListener("message", listener);
79-
};
40+
useMessageFromExtension<ToMethodModelingMessage>((msg) => {
41+
switch (msg.t) {
42+
case "setMethodModelingPanelViewState":
43+
setViewState(msg.viewState);
44+
break;
45+
case "setInModelingMode":
46+
setInModelingMode(msg.inModelingMode);
47+
break;
48+
case "setMultipleModeledMethods":
49+
setModeledMethods(msg.modeledMethods);
50+
break;
51+
case "setMethodModified":
52+
setIsMethodModified(msg.isModified);
53+
break;
54+
case "setNoMethodSelected":
55+
setMethod(undefined);
56+
setModeledMethods([]);
57+
setIsMethodModified(false);
58+
break;
59+
case "setSelectedMethod":
60+
setMethod(msg.method);
61+
setModeledMethods(msg.modeledMethods);
62+
setIsMethodModified(msg.isModified);
63+
break;
64+
default:
65+
assertNever(msg);
66+
}
8067
}, []);
8168

8269
if (!inModelingMode || !viewState?.language) {

0 commit comments

Comments
 (0)