Skip to content

Commit d05d280

Browse files
authored
web reporter refactor and issueFormService (#212951)
* web version working * change to mainWindow * PROPER MOVEMENT * working for web as well * move issueFormService to workbench/contrib/issue * cleaning up{ * more cleanup, added setting * styling * use mainwindow to open and closee * css fixes * fix css again * fix CSS and wonky applyCSS rules * change gulpfile * add and update system info * address some of the comments * move files! small changes * move JS and non window specific back to electron sandbox * fix on issueReporter.js * fix build file * fix gulp file too.... * move everything into contrib * fix workbench import * move everything else into contrib, fix import * change name to web * applying more feedback fixes :D * fix command and remove unused import: * add back issueTroubleshoot * fix gulpile outputs * fix out exclusion:
1 parent 0d35008 commit d05d280

31 files changed

+949
-121
lines changed

build/gulpfile.vscode.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,8 @@ const vscodeResources = [
7373
'out-build/vs/workbench/contrib/terminal/browser/media/*.sh',
7474
'out-build/vs/workbench/contrib/terminal/browser/media/*.zsh',
7575
'out-build/vs/workbench/contrib/webview/browser/pre/*.js',
76+
'!out-build/vs/workbench/contrib/issue/browser/*.html',
77+
'!out-build/vs/workbench/contrib/issue/**/*-dev.html',
7678
'out-build/vs/**/markdown.css',
7779
'out-build/vs/workbench/contrib/tasks/**/*.json',
7880
'!**/test/**'
@@ -122,7 +124,8 @@ const optimizeVSCodeTask = task.define('optimize-vscode', task.series(
122124
},
123125
manual: [
124126
{ src: [...windowBootstrapFiles, 'out-build/vs/code/electron-sandbox/workbench/workbench.js'], out: 'vs/code/electron-sandbox/workbench/workbench.js' },
125-
{ src: [...windowBootstrapFiles, 'out-build/vs/code/electron-sandbox/issue/issueReporter.js'], out: 'vs/code/electron-sandbox/issue/issueReporter.js' },
127+
// TODO: @justchen https://github.com/microsoft/vscode/issues/213332 make sure to remove when we use window.open on desktop.
128+
{ src: [...windowBootstrapFiles, 'out-build/vs/workbench/contrib/issue/electron-sandbox/issueReporter.js'], out: 'vs/workbench/contrib/issue/electron-sandbox/issueReporter.js' },
126129
{ src: [...windowBootstrapFiles, 'out-build/vs/code/electron-sandbox/processExplorer/processExplorer.js'], out: 'vs/code/electron-sandbox/processExplorer/processExplorer.js' }
127130
]
128131
}

src/buildfile.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,8 @@ exports.workbenchDesktop = [
5757
createModuleDescription('vs/workbench/contrib/debug/node/telemetryApp'),
5858
createModuleDescription('vs/platform/files/node/watcher/watcherMain'),
5959
createModuleDescription('vs/platform/terminal/node/ptyHostMain'),
60-
createModuleDescription('vs/workbench/api/node/extensionHostProcess')
60+
createModuleDescription('vs/workbench/api/node/extensionHostProcess'),
61+
createModuleDescription('vs/workbench/contrib/issue/electron-sandbox/issueReporterMain'),
6162
];
6263

6364
exports.workbenchWeb = [
@@ -76,7 +77,6 @@ exports.code = [
7677
createModuleDescription('vs/code/electron-main/main'),
7778
createModuleDescription('vs/code/node/cli'),
7879
createModuleDescription('vs/code/node/cliProcessMain', ['vs/code/node/cli']),
79-
createModuleDescription('vs/code/electron-sandbox/issue/issueReporterMain'),
8080
createModuleDescription('vs/code/node/sharedProcess/sharedProcessMain'),
8181
createModuleDescription('vs/code/electron-sandbox/processExplorer/processExplorerMain')
8282
];

src/vs/platform/issue/electron-main/issueMainService.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@ export class IssueMainService implements IIssueMainService {
173173
});
174174

175175
this.issueReporterWindow.loadURL(
176-
FileAccess.asBrowserUri(`vs/code/electron-sandbox/issue/issueReporter${this.environmentMainService.isBuilt ? '' : '-dev'}.html`).toString(true)
176+
FileAccess.asBrowserUri(`vs/workbench/contrib/issue/electron-sandbox/issueReporter${this.environmentMainService.isBuilt ? '' : '-dev'}.html`).toString(true)
177177
);
178178

179179
this.issueReporterWindow.on('close', () => {

src/vs/workbench/contrib/extensions/common/reportExtensionIssueAction.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import * as nls from 'vs/nls';
77
import { Action } from 'vs/base/common/actions';
88
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
9-
import { IWorkbenchIssueService } from 'vs/workbench/services/issue/common/issue';
9+
import { IWorkbenchIssueService } from 'vs/workbench/contrib/issue/common/issue';
1010

1111
export class ReportExtensionIssueAction extends Action {
1212

src/vs/workbench/contrib/issue/browser/issue.contribution.ts

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,14 @@ import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle
1010
import { IProductService } from 'vs/platform/product/common/productService';
1111
import { Registry } from 'vs/platform/registry/common/platform';
1212
import { Extensions, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions';
13-
import { WebIssueService } from 'vs/workbench/services/issue/browser/issueService';
14-
import { IWorkbenchIssueService } from 'vs/workbench/services/issue/common/issue';
13+
import { BrowserIssueService } from 'vs/workbench/contrib/issue/browser/issueService';
14+
import { IWorkbenchIssueService } from 'vs/workbench/contrib/issue/common/issue';
1515
import { BaseIssueContribution } from 'vs/workbench/contrib/issue/common/issue.contribution';
1616
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
17+
import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from 'vs/platform/configuration/common/configurationRegistry';
18+
import { IIssueMainService } from 'vs/platform/issue/common/issue';
19+
import { IssueFormService } from 'vs/workbench/contrib/issue/browser/issueFormService';
20+
import 'vs/workbench/contrib/issue/browser/issueTroubleshoot';
1721

1822

1923
class WebIssueContribution extends BaseIssueContribution {
@@ -24,8 +28,20 @@ class WebIssueContribution extends BaseIssueContribution {
2428

2529
Registry.as<IWorkbenchContributionsRegistry>(Extensions.Workbench).registerWorkbenchContribution(WebIssueContribution, LifecyclePhase.Restored);
2630

27-
registerSingleton(IWorkbenchIssueService, WebIssueService, InstantiationType.Delayed);
31+
registerSingleton(IWorkbenchIssueService, BrowserIssueService, InstantiationType.Delayed);
32+
registerSingleton(IIssueMainService, IssueFormService, InstantiationType.Delayed);
2833

2934
CommandsRegistry.registerCommand('_issues.getSystemStatus', (accessor) => {
3035
return nls.localize('statusUnsupported', "The --status argument is not yet supported in browsers.");
3136
});
37+
38+
Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration)
39+
.registerConfiguration({
40+
properties: {
41+
'issueReporter.experimental.webReporter': {
42+
type: 'boolean',
43+
default: true,
44+
description: 'Enable experimental issue reporter for web.',
45+
},
46+
}
47+
});

src/vs/code/browser/issue/issue.ts renamed to src/vs/workbench/contrib/issue/browser/issue.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,12 @@ import { CancellationError } from 'vs/base/common/errors';
1414
import { isLinuxSnap } from 'vs/base/common/platform';
1515
import { escape } from 'vs/base/common/strings';
1616
import { URI } from 'vs/base/common/uri';
17-
import { IssueReporterModel, IssueReporterData as IssueReporterModelData } from 'vs/code/browser/issue/issueReporterModel';
1817
import { localize } from 'vs/nls';
1918
import { IIssueMainService, IssueReporterData, IssueReporterExtensionData, IssueReporterStyles, IssueType } from 'vs/platform/issue/common/issue';
2019
import { normalizeGitHubUrl } from 'vs/platform/issue/common/issueReporterUtil';
2120
import { getIconsStyleSheet } from 'vs/platform/theme/browser/iconsStyleSheet';
21+
import { IssueReporterModel, IssueReporterData as IssueReporterModelData } from 'vs/workbench/contrib/issue/browser/issueReporterModel';
22+
import { mainWindow } from 'vs/base/browser/window';
2223

2324
const MAX_URL_LENGTH = 7500;
2425

@@ -156,7 +157,7 @@ export class BaseIssueReporterService extends Disposable {
156157
const content: string[] = [];
157158

158159
if (styles.inputBackground) {
159-
content.push(`input[type="text"], textarea, select, .issues-container > .issue > .issue-state, .block-info { background-color: ${styles.inputBackground}; }`);
160+
content.push(`input[type="text"], textarea, select, .issues-container > .issue > .issue-state, .block-info { background-color: ${styles.inputBackground} !important; }`);
160161
}
161162

162163
if (styles.inputBorder) {
@@ -166,7 +167,7 @@ export class BaseIssueReporterService extends Disposable {
166167
}
167168

168169
if (styles.inputForeground) {
169-
content.push(`input[type="text"], textarea, select, .issues-container > .issue > .issue-state, .block-info { color: ${styles.inputForeground}; }`);
170+
content.push(`input[type="text"], textarea, select, .issues-container > .issue > .issue-state, .block-info { color: ${styles.inputForeground} !important; }`);
170171
}
171172

172173
if (styles.inputErrorBorder) {
@@ -825,7 +826,8 @@ export class BaseIssueReporterService extends Disposable {
825826
}),
826827
headers: new Headers({
827828
'Content-Type': 'application/json',
828-
'Authorization': `Bearer ${this.data.githubAccessToken}`
829+
'Authorization': `Bearer ${this.data.githubAccessToken}`,
830+
'User-Agent': 'request'
829831
})
830832
};
831833

@@ -835,8 +837,7 @@ export class BaseIssueReporterService extends Disposable {
835837
return false;
836838
}
837839
const result = await response.json();
838-
this.window.open(result.html_url, '_blank');
839-
840+
mainWindow.open(result.html_url, '_blank');
840841
this.close();
841842
return true;
842843
}
Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
* Licensed under the MIT License. See License.txt in the project root for license information.
4+
*--------------------------------------------------------------------------------------------*/
5+
import { safeInnerHtml } from 'vs/base/browser/dom';
6+
import { mainWindow } from 'vs/base/browser/window';
7+
import { DisposableStore } from 'vs/base/common/lifecycle';
8+
import { URI } from 'vs/base/common/uri';
9+
import BaseHtml from 'vs/workbench/contrib/issue/browser/issueReporterPage';
10+
import 'vs/css!./media/issueReporter';
11+
import { IMenuService, MenuId } from 'vs/platform/actions/common/actions';
12+
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
13+
import { PerformanceInfo, SystemInfo } from 'vs/platform/diagnostics/common/diagnostics';
14+
import { ExtensionIdentifier, ExtensionIdentifierSet } from 'vs/platform/extensions/common/extensions';
15+
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
16+
import { IIssueMainService, IssueReporterData, ProcessExplorerData } from 'vs/platform/issue/common/issue';
17+
import product from 'vs/platform/product/common/product';
18+
import { IssueWebReporter } from 'vs/workbench/contrib/issue/browser/issueReporterService';
19+
import { AuxiliaryWindowMode, IAuxiliaryWindowService } from 'vs/workbench/services/auxiliaryWindow/browser/auxiliaryWindowService';
20+
21+
export class IssueFormService implements IIssueMainService {
22+
23+
readonly _serviceBrand: undefined;
24+
25+
private issueReporterWindow: Window | null = null;
26+
private extensionIdentifierSet: ExtensionIdentifierSet = new ExtensionIdentifierSet();
27+
28+
constructor(
29+
@IInstantiationService private readonly instantiationService: IInstantiationService,
30+
@IAuxiliaryWindowService private readonly auxiliaryWindowService: IAuxiliaryWindowService,
31+
@IMenuService private readonly menuService: IMenuService,
32+
@IContextKeyService private readonly contextKeyService: IContextKeyService,
33+
) {
34+
35+
// listen for messages from the main window
36+
mainWindow.addEventListener('message', async (event) => {
37+
if (event.data && event.data.sendChannel === 'vscode:triggerReporterMenu') {
38+
// creates menu from contributed
39+
const menu = this.menuService.createMenu(MenuId.IssueReporter, this.contextKeyService);
40+
41+
// render menu and dispose
42+
const actions = menu.getActions({ renderShortTitle: true }).flatMap(entry => entry[1]);
43+
for (const action of actions) {
44+
try {
45+
if (action.item && 'source' in action.item && action.item.source?.id === event.data.extensionId) {
46+
this.extensionIdentifierSet.add(event.data.extensionId);
47+
await action.run();
48+
}
49+
} catch (error) {
50+
console.error(error);
51+
}
52+
}
53+
54+
if (!this.extensionIdentifierSet.has(event.data.extensionId)) {
55+
// send undefined to indicate no action was taken
56+
const replyChannel = `vscode:triggerReporterMenuResponse`;
57+
mainWindow.postMessage({ replyChannel }, '*');
58+
}
59+
60+
menu.dispose();
61+
}
62+
});
63+
64+
}
65+
66+
async openReporter(data: IssueReporterData): Promise<void> {
67+
if (data.extensionId && this.extensionIdentifierSet.has(data.extensionId)) {
68+
const replyChannel = `vscode:triggerReporterMenuResponse`;
69+
mainWindow.postMessage({ data, replyChannel }, '*');
70+
this.extensionIdentifierSet.delete(new ExtensionIdentifier(data.extensionId));
71+
}
72+
73+
if (this.issueReporterWindow) {
74+
this.issueReporterWindow.focus();
75+
return;
76+
}
77+
78+
const disposables = new DisposableStore();
79+
80+
// Auxiliary Window
81+
const auxiliaryWindow = disposables.add(await this.auxiliaryWindowService.open({ mode: AuxiliaryWindowMode.Normal }));
82+
83+
this.issueReporterWindow = auxiliaryWindow.window;
84+
85+
86+
87+
if (auxiliaryWindow) {
88+
await auxiliaryWindow.whenStylesHaveLoaded;
89+
auxiliaryWindow.window.document.title = 'Issue Reporter';
90+
auxiliaryWindow.window.document.body.classList.add('issue-reporter-body');
91+
92+
// custom issue reporter wrapper
93+
const div = document.createElement('div');
94+
div.classList.add('monaco-workbench');
95+
96+
// removes preset monaco-workbench
97+
auxiliaryWindow.container.remove();
98+
auxiliaryWindow.window.document.body.appendChild(div);
99+
safeInnerHtml(div, BaseHtml());
100+
101+
// create issue reporter and instantiate
102+
const issueReporter = this.instantiationService.createInstance(IssueWebReporter, false, data, { type: '', arch: '', release: '' }, product, auxiliaryWindow.window);
103+
issueReporter.render();
104+
} else {
105+
console.error('Failed to open auxiliary window');
106+
}
107+
108+
// handle closing issue reporter
109+
this.issueReporterWindow?.addEventListener('beforeunload', () => {
110+
auxiliaryWindow.window.close();
111+
this.issueReporterWindow = null;
112+
});
113+
}
114+
115+
async openProcessExplorer(data: ProcessExplorerData): Promise<void> {
116+
throw new Error('Method not implemented.');
117+
}
118+
119+
stopTracing(): Promise<void> {
120+
throw new Error('Method not implemented.');
121+
}
122+
getSystemStatus(): Promise<string> {
123+
throw new Error('Method not implemented.');
124+
}
125+
$getSystemInfo(): Promise<SystemInfo> {
126+
throw new Error('Method not implemented.');
127+
}
128+
$getPerformanceInfo(): Promise<PerformanceInfo> {
129+
throw new Error('Method not implemented.');
130+
}
131+
$reloadWithExtensionsDisabled(): Promise<void> {
132+
throw new Error('Method not implemented.');
133+
}
134+
$showConfirmCloseDialog(): Promise<void> {
135+
throw new Error('Method not implemented.');
136+
}
137+
$showClipboardDialog(): Promise<boolean> {
138+
throw new Error('Method not implemented.');
139+
}
140+
$getIssueReporterUri(extensionId: string): Promise<URI> {
141+
throw new Error('Method not implemented.');
142+
}
143+
$getIssueReporterData(extensionId: string): Promise<string> {
144+
throw new Error('Method not implemented.');
145+
}
146+
$getIssueReporterTemplate(extensionId: string): Promise<string> {
147+
throw new Error('Method not implemented.');
148+
}
149+
$getReporterStatus(extensionId: string, extensionName: string): Promise<boolean[]> {
150+
throw new Error('Method not implemented.');
151+
}
152+
153+
async $sendReporterMenu(extensionId: string, extensionName: string): Promise<IssueReporterData | undefined> {
154+
const sendChannel = `vscode:triggerReporterMenu`;
155+
mainWindow.postMessage({ sendChannel, extensionId, extensionName }, '*');
156+
157+
const result = await new Promise((resolve, reject) => {
158+
const timeout = setTimeout(() => {
159+
mainWindow.removeEventListener('message', listener);
160+
reject(new Error('Timeout exceeded'));
161+
}, 5000); // Set the timeout value in milliseconds (e.g., 5000 for 5 seconds)
162+
163+
const listener = (event: MessageEvent) => {
164+
const replyChannel = `vscode:triggerReporterMenuResponse`;
165+
if (event.data && event.data.replyChannel === replyChannel) {
166+
clearTimeout(timeout);
167+
mainWindow.removeEventListener('message', listener);
168+
resolve(event.data.data);
169+
}
170+
};
171+
mainWindow.addEventListener('message', listener);
172+
});
173+
174+
return result as IssueReporterData | undefined;
175+
}
176+
177+
async $closeReporter(): Promise<void> {
178+
this.issueReporterWindow?.close();
179+
}
180+
}

src/vs/code/browser/issue/issueReporterModel.ts renamed to src/vs/workbench/contrib/issue/browser/issueReporterModel.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ export interface IssueReporterData {
1313

1414
versionInfo?: any;
1515
systemInfo?: SystemInfo;
16+
systemInfoWeb?: string;
1617
processInfo?: string;
1718
workspaceInfo?: string;
1819

@@ -138,6 +139,10 @@ ${this.getInfos()}
138139
if (this._data.includeSystemInfo && this._data.systemInfo) {
139140
info += this.generateSystemInfoMd();
140141
}
142+
143+
if (this._data.includeSystemInfo && this._data.systemInfoWeb) {
144+
info += 'System Info: ' + this._data.systemInfoWeb;
145+
}
141146
}
142147

143148
if (this._data.issueType === IssueType.PerformanceIssue) {

src/vs/code/browser/issue/issueReporterPage.ts renamed to src/vs/workbench/contrib/issue/browser/issueReporterPage.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ const reviewGuidanceLabel = localize( // intentionally not escaped because of it
2424
);
2525

2626
export default (): string => `
27-
<div id="issue-reporter">
27+
<div class="issue-reporter" id="issue-reporter">
2828
<div id="english" class="input-group hidden">${escape(localize('completeInEnglish', "Please complete the form in English."))}</div>
2929
3030
<div id="review-guidance-help-text" class="input-group">${reviewGuidanceLabel}</div>

0 commit comments

Comments
 (0)