mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
In-app survey extension (#1945)
* Initial in-app survey implementation Signed-off-by: Lauri Nevala <lauri.nevala@gmail.com> * Add surveyId fetching and store integration Signed-off-by: Lauri Nevala <lauri.nevala@gmail.com> * Add empty line Signed-off-by: Lauri Nevala <lauri.nevala@gmail.com> * Fix typos Signed-off-by: Lauri Nevala <lauri.nevala@gmail.com> * Use async version of machineId + refactoring Signed-off-by: Lauri Nevala <lauri.nevala@gmail.com> * Update preferences hint text Signed-off-by: Lauri Nevala <lauri.nevala@gmail.com> Co-authored-by: Jari Kolehmainen <jari.kolehmainen@gmail.com>
This commit is contained in:
parent
5287e7e528
commit
a0e24e0a4d
9
extensions/survey/main.ts
Normal file
9
extensions/survey/main.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import { LensMainExtension } from "@k8slens/extensions";
|
||||||
|
import { surveyPreferencesStore } from "./src/survey-preferences-store";
|
||||||
|
|
||||||
|
export default class SurveyMainExtension extends LensMainExtension {
|
||||||
|
|
||||||
|
async onActivate() {
|
||||||
|
await surveyPreferencesStore.loadExtension(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
7928
extensions/survey/package-lock.json
generated
Normal file
7928
extensions/survey/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
28
extensions/survey/package.json
Normal file
28
extensions/survey/package.json
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
{
|
||||||
|
"name": "lens-survey",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"description": "Lens survey",
|
||||||
|
"main": "dist/main.js",
|
||||||
|
"renderer": "dist/renderer.js",
|
||||||
|
"lens": {
|
||||||
|
"metadata": {},
|
||||||
|
"styles": []
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"build": "webpack -p",
|
||||||
|
"dev": "webpack --watch",
|
||||||
|
"test": "jest --passWithNoTests --env=jsdom src $@"
|
||||||
|
},
|
||||||
|
"dependencies": {},
|
||||||
|
"devDependencies": {
|
||||||
|
"@k8slens/extensions": "file:../../src/extensions/npm/extensions",
|
||||||
|
"got": "^11.8.1",
|
||||||
|
"jest": "^26.6.3",
|
||||||
|
"node-machine-id": "^1.1.12",
|
||||||
|
"react": "^16.13.1",
|
||||||
|
"refiner-js": "^1.0.1",
|
||||||
|
"ts-loader": "^8.0.4",
|
||||||
|
"typescript": "^4.0.3",
|
||||||
|
"webpack": "^4.44.2"
|
||||||
|
}
|
||||||
|
}
|
||||||
21
extensions/survey/renderer.tsx
Normal file
21
extensions/survey/renderer.tsx
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import { LensRendererExtension } from "@k8slens/extensions";
|
||||||
|
import { survey } from "./src/survey";
|
||||||
|
import { SurveyPreferenceHint, SurveyPreferenceInput } from "./src/survey-preference";
|
||||||
|
import { surveyPreferencesStore } from "./src/survey-preferences-store";
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
|
export default class SurveyRendererExtension extends LensRendererExtension {
|
||||||
|
appPreferences = [
|
||||||
|
{
|
||||||
|
title: "In-App Surveys",
|
||||||
|
components: {
|
||||||
|
Hint: () => <SurveyPreferenceHint/>,
|
||||||
|
Input: () => <SurveyPreferenceInput survey={surveyPreferencesStore}/>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
];
|
||||||
|
async onActivate() {
|
||||||
|
await surveyPreferencesStore.loadExtension(this);
|
||||||
|
survey.start();
|
||||||
|
}
|
||||||
|
}
|
||||||
3
extensions/survey/src/refiner-js.d.ts
vendored
Normal file
3
extensions/survey/src/refiner-js.d.ts
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
declare module "refiner-js" {
|
||||||
|
export default function Refiner(key: string, value: string|object|number|Boolean|Array): void;
|
||||||
|
}
|
||||||
27
extensions/survey/src/survey-preference.tsx
Normal file
27
extensions/survey/src/survey-preference.tsx
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import { Component } from "@k8slens/extensions";
|
||||||
|
import React from "react";
|
||||||
|
import { observer } from "mobx-react";
|
||||||
|
import { SurveyPreferencesStore } from "./survey-preferences-store";
|
||||||
|
|
||||||
|
@observer
|
||||||
|
export class SurveyPreferenceInput extends React.Component<{survey: SurveyPreferencesStore}, {}> {
|
||||||
|
render() {
|
||||||
|
const { survey } = this.props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Component.Checkbox
|
||||||
|
label="Allow in-app surveys"
|
||||||
|
value={survey.enabled}
|
||||||
|
onChange={v => survey.enabled = v }
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class SurveyPreferenceHint extends React.Component {
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<span>This will allow you to participate in surveys to improve the Lens experience.</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
36
extensions/survey/src/survey-preferences-store.ts
Normal file
36
extensions/survey/src/survey-preferences-store.ts
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
import { Store } from "@k8slens/extensions";
|
||||||
|
import { observable, toJS, when } from "mobx";
|
||||||
|
|
||||||
|
export type SurveyPreferencesModel = {
|
||||||
|
enabled: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export class SurveyPreferencesStore extends Store.ExtensionStore<SurveyPreferencesModel> {
|
||||||
|
|
||||||
|
@observable enabled = true;
|
||||||
|
|
||||||
|
whenEnabled = when(() => this.enabled);
|
||||||
|
|
||||||
|
private constructor() {
|
||||||
|
super({
|
||||||
|
configName: "preferences-store",
|
||||||
|
defaults: {
|
||||||
|
enabled: true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fromStore({ enabled }: SurveyPreferencesModel): void {
|
||||||
|
this.enabled = enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
toJSON(): SurveyPreferencesModel {
|
||||||
|
return toJS({
|
||||||
|
enabled: this.enabled
|
||||||
|
}, {
|
||||||
|
recurseEverything: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const surveyPreferencesStore = SurveyPreferencesStore.getInstance<SurveyPreferencesStore>();
|
||||||
46
extensions/survey/src/survey.ts
Normal file
46
extensions/survey/src/survey.ts
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
import { Util } from "@k8slens/extensions";
|
||||||
|
import { machineId } from "node-machine-id";
|
||||||
|
import Refiner from "refiner-js";
|
||||||
|
import got from "got";
|
||||||
|
import { surveyPreferencesStore } from "./survey-preferences-store";
|
||||||
|
|
||||||
|
type SurveyIdResponse = {
|
||||||
|
surveyId: string;
|
||||||
|
};
|
||||||
|
export class Survey extends Util.Singleton {
|
||||||
|
static readonly PROJECT_ID = "af468d00-4f8f-11eb-b01d-23b6562fef43";
|
||||||
|
protected anonymousId: string;
|
||||||
|
|
||||||
|
private constructor() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
async start() {
|
||||||
|
await surveyPreferencesStore.whenEnabled;
|
||||||
|
|
||||||
|
const surveyId = await this.fetchSurveyId();
|
||||||
|
|
||||||
|
if (surveyId) {
|
||||||
|
Refiner("setProject", Survey.PROJECT_ID);
|
||||||
|
Refiner("identifyUser", {
|
||||||
|
id: surveyId,
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fetchSurveyId() {
|
||||||
|
try {
|
||||||
|
const surveyApi = process.env.SURVEY_API_URL || "https://survey.k8slens.dev";
|
||||||
|
const anonymousId = await machineId();
|
||||||
|
const { body } = await got(`${surveyApi}/api/survey-id?anonymousId=${anonymousId}`, { responseType: "json"});
|
||||||
|
|
||||||
|
return (body as SurveyIdResponse).surveyId;
|
||||||
|
} catch(error) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const survey = Survey.getInstance<Survey>();
|
||||||
29
extensions/survey/tsconfig.json
Normal file
29
extensions/survey/tsconfig.json
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "dist",
|
||||||
|
"baseUrl": ".",
|
||||||
|
"module": "CommonJS",
|
||||||
|
"target": "ES2017",
|
||||||
|
"lib": ["ESNext", "DOM", "DOM.Iterable"],
|
||||||
|
"moduleResolution": "Node",
|
||||||
|
"sourceMap": false,
|
||||||
|
"declaration": false,
|
||||||
|
"strict": false,
|
||||||
|
"noImplicitAny": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"allowSyntheticDefaultImports": true,
|
||||||
|
"experimentalDecorators": true,
|
||||||
|
"jsx": "react",
|
||||||
|
"paths": {
|
||||||
|
"*": [
|
||||||
|
"node_modules/*",
|
||||||
|
"../../types/*"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"renderer.ts",
|
||||||
|
"src/**/*"
|
||||||
|
]
|
||||||
|
}
|
||||||
67
extensions/survey/webpack.config.js
Normal file
67
extensions/survey/webpack.config.js
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
const path = require("path");
|
||||||
|
|
||||||
|
module.exports = [
|
||||||
|
{
|
||||||
|
entry: "./main.ts",
|
||||||
|
context: __dirname,
|
||||||
|
target: "electron-main",
|
||||||
|
mode: "production",
|
||||||
|
module: {
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
test: /\.tsx?$/,
|
||||||
|
use: "ts-loader",
|
||||||
|
exclude: /node_modules/,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
externals: [
|
||||||
|
{
|
||||||
|
"@k8slens/extensions": "var global.LensExtensions",
|
||||||
|
"react": "var global.React",
|
||||||
|
"mobx": "var global.Mobx"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
resolve: {
|
||||||
|
extensions: [ ".tsx", ".ts", ".js" ],
|
||||||
|
},
|
||||||
|
output: {
|
||||||
|
libraryTarget: "commonjs2",
|
||||||
|
globalObject: "this",
|
||||||
|
filename: "main.js",
|
||||||
|
path: path.resolve(__dirname, "dist"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
entry: "./renderer.tsx",
|
||||||
|
context: __dirname,
|
||||||
|
target: "electron-renderer",
|
||||||
|
mode: "production",
|
||||||
|
module: {
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
test: /\.tsx?$/,
|
||||||
|
use: "ts-loader",
|
||||||
|
exclude: /node_modules/,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
externals: [
|
||||||
|
{
|
||||||
|
"@k8slens/extensions": "var global.LensExtensions",
|
||||||
|
"react": "var global.React",
|
||||||
|
"mobx": "var global.Mobx",
|
||||||
|
"mobx-react": "var global.MobxReact"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
resolve: {
|
||||||
|
extensions: [ ".tsx", ".ts", ".js" ],
|
||||||
|
},
|
||||||
|
output: {
|
||||||
|
libraryTarget: "commonjs2",
|
||||||
|
globalObject: "this",
|
||||||
|
filename: "renderer.js",
|
||||||
|
path: path.resolve(__dirname, "dist"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
@ -179,7 +179,8 @@
|
|||||||
"node-menu",
|
"node-menu",
|
||||||
"metrics-cluster-feature",
|
"metrics-cluster-feature",
|
||||||
"license-menu-item",
|
"license-menu-item",
|
||||||
"kube-object-event-status"
|
"kube-object-event-status",
|
||||||
|
"survey"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user