1
0
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:
Lauri Nevala 2021-02-04 18:22:22 +02:00 committed by GitHub
parent 5287e7e528
commit a0e24e0a4d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 8196 additions and 1 deletions

View 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

File diff suppressed because it is too large Load Diff

View 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"
}
}

View 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
View File

@ -0,0 +1,3 @@
declare module "refiner-js" {
export default function Refiner(key: string, value: string|object|number|Boolean|Array): void;
}

View 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>
);
}
}

View 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>();

View 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>();

View 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/**/*"
]
}

View 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"),
},
},
];

View File

@ -179,7 +179,8 @@
"node-menu",
"metrics-cluster-feature",
"license-menu-item",
"kube-object-event-status"
"kube-object-event-status",
"survey"
]
},
"dependencies": {