Skip to content

Commit f5cee97

Browse files
Implement survey notification (#1035)
1 parent a9aac0d commit f5cee97

File tree

7 files changed

+160
-0
lines changed

7 files changed

+160
-0
lines changed

Diff for: arduino-ide-extension/src/browser/arduino-ide-frontend-module.ts

+18
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@ import { SaveAsSketch } from './contributions/save-as-sketch';
121121
import { SaveSketch } from './contributions/save-sketch';
122122
import { VerifySketch } from './contributions/verify-sketch';
123123
import { UploadSketch } from './contributions/upload-sketch';
124+
import { SurveyNotification } from './contributions/survey-notification';
124125
import { CommonFrontendContribution } from './theia/core/common-frontend-contribution';
125126
import { EditContributions } from './contributions/edit-contributions';
126127
import { OpenSketchExternal } from './contributions/open-sketch-external';
@@ -291,6 +292,10 @@ import { PreferenceTreeGenerator } from './theia/preferences/preference-tree-gen
291292
import { PreferenceTreeGenerator as TheiaPreferenceTreeGenerator } from '@theia/preferences/lib/browser/util/preference-tree-generator';
292293
import { AboutDialog } from './theia/core/about-dialog';
293294
import { AboutDialog as TheiaAboutDialog } from '@theia/core/lib/browser/about-dialog';
295+
import {
296+
SurveyNotificationService,
297+
SurveyNotificationServicePath,
298+
} from '../common/protocol/survey-service';
294299

295300
MonacoThemingService.register({
296301
id: 'arduino-theme',
@@ -475,6 +480,19 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
475480
bind(EditorMode).toSelf().inSingletonScope();
476481
bind(FrontendApplicationContribution).toService(EditorMode);
477482

483+
// Survey notification
484+
bind(SurveyNotification).toSelf().inSingletonScope();
485+
bind(FrontendApplicationContribution).toService(SurveyNotification);
486+
487+
bind(SurveyNotificationService)
488+
.toDynamicValue((context) => {
489+
return ElectronIpcConnectionProvider.createProxy(
490+
context.container,
491+
SurveyNotificationServicePath
492+
);
493+
})
494+
.inSingletonScope();
495+
478496
// Layout and shell customizations.
479497
rebind(TheiaOutlineViewContribution)
480498
.to(OutlineViewContribution)

Diff for: arduino-ide-extension/src/browser/arduino-preferences.ts

+9
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,14 @@ export const ArduinoConfigSchema: PreferenceSchema = {
174174
),
175175
default: 'https://auth.arduino.cc/login#/register',
176176
},
177+
'arduino.survey.notification': {
178+
type: 'boolean',
179+
description: nls.localize(
180+
'arduino/preferences/survey.notification',
181+
'True if users should be notified if a survey is available. True by default.'
182+
),
183+
default: true,
184+
},
177185
},
178186
};
179187

@@ -198,6 +206,7 @@ export interface ArduinoConfiguration {
198206
'arduino.auth.domain': string;
199207
'arduino.auth.audience': string;
200208
'arduino.auth.registerUri': string;
209+
'arduino.survey.notification': boolean;
201210
}
202211

203212
export const ArduinoPreferences = Symbol('ArduinoPreferences');
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import { MessageService } from '@theia/core';
2+
import { FrontendApplicationContribution } from '@theia/core/lib/browser';
3+
import { inject, injectable } from '@theia/core/shared/inversify';
4+
import { LocalStorageService } from '@theia/core/lib/browser';
5+
import { nls } from '@theia/core/lib/common';
6+
import { WindowService } from '@theia/core/lib/browser/window/window-service';
7+
import { ArduinoPreferences } from '../arduino-preferences';
8+
import { SurveyNotificationService } from '../../common/protocol/survey-service';
9+
10+
const SURVEY_MESSAGE = nls.localize(
11+
'arduino/survey/surveyMessage',
12+
'Please help us improve by answering this super short survey. We value our community and would like to get to know our supporters a little better.'
13+
);
14+
const DO_NOT_SHOW_AGAIN = nls.localize(
15+
'arduino/survey/dismissSurvey',
16+
"Don't show again"
17+
);
18+
const GO_TO_SURVEY = nls.localize(
19+
'arduino/survey/answerSurvey',
20+
'Answer survey'
21+
);
22+
23+
const SURVEY_BASE_URL = 'https://surveys.hotjar.com/';
24+
const surveyId = '17887b40-e1f0-4bd6-b9f0-a37f229ccd8b';
25+
26+
@injectable()
27+
export class SurveyNotification implements FrontendApplicationContribution {
28+
@inject(MessageService)
29+
private readonly messageService: MessageService;
30+
31+
@inject(LocalStorageService)
32+
private readonly localStorageService: LocalStorageService;
33+
34+
@inject(WindowService)
35+
private readonly windowService: WindowService;
36+
37+
@inject(ArduinoPreferences)
38+
private readonly arduinoPreferences: ArduinoPreferences;
39+
40+
@inject(SurveyNotificationService)
41+
private readonly surveyNotificationService: SurveyNotificationService;
42+
43+
onStart(): void {
44+
this.arduinoPreferences.ready.then(async () => {
45+
if (
46+
(await this.surveyNotificationService.isFirstInstance()) &&
47+
this.arduinoPreferences.get('arduino.survey.notification')
48+
) {
49+
const surveyAnswered = await this.localStorageService.getData(
50+
this.surveyKey(surveyId)
51+
);
52+
if (surveyAnswered !== undefined) {
53+
return;
54+
}
55+
const answer = await this.messageService.info(
56+
SURVEY_MESSAGE,
57+
DO_NOT_SHOW_AGAIN,
58+
GO_TO_SURVEY
59+
);
60+
switch (answer) {
61+
case GO_TO_SURVEY:
62+
this.windowService.openNewWindow(SURVEY_BASE_URL + surveyId, {
63+
external: true,
64+
});
65+
this.localStorageService.setData(this.surveyKey(surveyId), true);
66+
break;
67+
case DO_NOT_SHOW_AGAIN:
68+
this.localStorageService.setData(this.surveyKey(surveyId), false);
69+
break;
70+
}
71+
}
72+
});
73+
}
74+
75+
private surveyKey(id: string): string {
76+
return `answered_survey:${id}`;
77+
}
78+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export const SurveyNotificationServicePath =
2+
'/services/survey-notification-service';
3+
export const SurveyNotificationService = Symbol('SurveyNotificationService');
4+
5+
export interface SurveyNotificationService {
6+
isFirstInstance(): Promise<boolean>;
7+
}

Diff for: arduino-ide-extension/src/electron-main/arduino-electron-main-module.ts

+22
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,11 @@ import {
2121
import { IDEUpdaterImpl } from './ide-updater/ide-updater-impl';
2222
import { TheiaElectronWindow } from './theia/theia-electron-window';
2323
import { TheiaElectronWindow as DefaultTheiaElectronWindow } from '@theia/core/lib/electron-main/theia-electron-window';
24+
import { SurveyNotificationServiceImpl } from '../node/survey-service-impl';
25+
import {
26+
SurveyNotificationService,
27+
SurveyNotificationServicePath,
28+
} from '../common/protocol/survey-service';
2429

2530
export default new ContainerModule((bind, unbind, isBound, rebind) => {
2631
bind(ElectronMainApplication).toSelf().inSingletonScope();
@@ -61,4 +66,21 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
6166

6267
bind(TheiaElectronWindow).toSelf();
6368
rebind(DefaultTheiaElectronWindow).toService(TheiaElectronWindow);
69+
70+
// Survey notification bindings
71+
bind(SurveyNotificationServiceImpl).toSelf().inSingletonScope();
72+
bind(SurveyNotificationService).toService(SurveyNotificationServiceImpl);
73+
bind(ElectronMainApplicationContribution).toService(
74+
SurveyNotificationService
75+
);
76+
bind(ElectronConnectionHandler)
77+
.toDynamicValue(
78+
(context) =>
79+
new JsonRpcConnectionHandler(SurveyNotificationServicePath, () =>
80+
context.container.get<SurveyNotificationService>(
81+
SurveyNotificationService
82+
)
83+
)
84+
)
85+
.inSingletonScope();
6486
});
+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { injectable } from '@theia/core/shared/inversify';
2+
import { SurveyNotificationService } from '../common/protocol/survey-service';
3+
4+
/**
5+
* Service for checking if it is the first instance of the IDE, in this case it sets a flag to true.
6+
* This flag is used to prevent the survey notification from being visible in every open window. It must only be shown on one window.
7+
*/
8+
@injectable()
9+
export class SurveyNotificationServiceImpl
10+
implements SurveyNotificationService
11+
{
12+
private surveyDidShow = false;
13+
async isFirstInstance(): Promise<boolean> {
14+
if (this.surveyDidShow) {
15+
return false;
16+
}
17+
this.surveyDidShow = true;
18+
return this.surveyDidShow;
19+
}
20+
}

Diff for: i18n/en.json

+6
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,7 @@
256256
"showVerbose": "Show verbose output during",
257257
"sketchbook.location": "Sketchbook location",
258258
"sketchbook.showAllFiles": "True to show all sketch files inside the sketch. It is false by default.",
259+
"survey.notification": "True if users should be notified if a survey is available. True by default.",
259260
"unofficialBoardSupport": "Click for a list of unofficial board support URLs",
260261
"upload": "upload",
261262
"upload.verbose": "True for verbose upload output. False by default.",
@@ -305,6 +306,11 @@
305306
"verify": "Verify",
306307
"verifyOrCompile": "Verify/Compile"
307308
},
309+
"survey": {
310+
"answerSurvey": "Answer survey",
311+
"dismissSurvey": "Don't show again",
312+
"surveyMessage": "Please help us improve by answering this super short survey. We value our community and would like to get to know our supporters a little better."
313+
},
308314
"upload": {
309315
"error": "{0} error: {1}"
310316
},

0 commit comments

Comments
 (0)