tracker / user-store refactoring, part 1

Signed-off-by: Roman <ixrock@gmail.com>
This commit is contained in:
Roman committed 2020-07-05 17:16:50 +03:00
1 parent c9fb015bcf
commit 4270ec4240
23 files changed
+247 -225

No files matched your search

+9 -9
View File
@@ -1,32 +1,32 @@
import ElectronStore from "electron-store"
import { Singleton } from "./utils/singleton";
import Config from "conf"
import Singleton from "./utils/singleton";
import migrations from "../migrations/cluster-store"
import { Cluster, ClusterBaseInfo } from "../main/cluster";
export class ClusterStore extends Singleton {
private store = new ElectronStore({
name: "lens-cluster-store",
private storeConfig = new Config({
configName: "lens-cluster-store",
accessPropertiesByDotNotation: false, // To make dots safe in cluster context names
migrations: migrations,
})
public getAllClusterObjects(): Cluster[] {
return this.store.get("clusters", []).map((clusterInfo: ClusterBaseInfo) => {
return this.storeConfig.get("clusters", []).map((clusterInfo: ClusterBaseInfo) => {
return new Cluster(clusterInfo)
})
}
public getAllClusters(): ClusterBaseInfo[] {
return this.store.get("clusters", [])
return this.storeConfig.get("clusters", [])
}
public removeCluster(id: string): void {
this.store.delete(id);
this.storeConfig.delete(id);
const clusterBaseInfos = this.getAllClusters()
const index = clusterBaseInfos.findIndex((cbi) => cbi.id === id)
if (index !== -1) {
clusterBaseInfos.splice(index, 1)
this.store.set("clusters", clusterBaseInfos)
this.storeConfig.set("clusters", clusterBaseInfos)
}
}
@@ -62,7 +62,7 @@ export class ClusterStore extends Singleton {
} else {
clusters[index] = storable
}
this.store.set("clusters", clusters)
this.storeConfig.set("clusters", clusters)
}
public storeClusters(clusters: ClusterBaseInfo[]) {
+5 -6
View File
@@ -1,12 +1,11 @@
import request from "request"
import { userStore } from "./user-store"
export function globalRequestOpts(requestOpts: request.Options ) {
const userPrefs = userStore.getPreferences()
if (userPrefs.httpsProxy) {
requestOpts.proxy = userPrefs.httpsProxy
export function globalRequestOpts(requestOpts: request.Options) {
const { httpsProxy, allowUntrustedCAs } = userStore.preferences
if (httpsProxy) {
requestOpts.proxy = httpsProxy
}
requestOpts.rejectUnauthorized = !userPrefs.allowUntrustedCAs;
requestOpts.rejectUnauthorized = !allowUntrustedCAs;
return requestOpts
}
+24 -18
View File
@@ -1,10 +1,12 @@
import { app, App, remote } from "electron"
import ua from "universal-analytics"
import { machineIdSync } from "node-machine-id"
import Singleton from "./utils/singleton";
import { userStore } from "./user-store"
const GA_ID = "UA-159377374-1"
export class Tracker extends Singleton {
static readonly GA_ID = "UA-159377374-1"
export class Tracker {
protected visitor: ua.Visitor
protected machineId: string = null;
protected ip: string = null;
@@ -12,31 +14,35 @@ export class Tracker {
protected locale: string;
protected electronUA: string;
constructor(app: Electron.App) {
private constructor(app: App) {
super();
try {
this.visitor = ua(GA_ID, machineIdSync(), {strictCidFormat: false})
this.visitor = ua(Tracker.GA_ID, machineIdSync(), { strictCidFormat: false })
} catch (error) {
this.visitor = ua(GA_ID)
this.visitor = ua(Tracker.GA_ID)
}
this.visitor.set("dl", "https://lensapptelemetry.lakendlabs.com")
}
public async event(eventCategory: string, eventAction: string) {
return new Promise(async (resolve, reject) => {
if (!this.telemetryAllowed()) {
resolve()
return
protected async isTelemetryAllowed(): Promise<boolean> {
return userStore.preferences.allowTelemetry;
}
async event(eventCategory: string, eventAction: string, otherParams = {}) {
try {
const allowed = await this.isTelemetryAllowed();
if (!allowed) {
return;
}
this.visitor.event({
ec: eventCategory,
ea: eventAction
ea: eventAction,
...otherParams,
}).send()
resolve()
})
}
protected telemetryAllowed() {
const userPrefs = userStore.getPreferences()
return !!userPrefs.allowTelemetry
} catch (err) {
console.error(`Failed to track "${eventCategory}:${eventAction}"`, err)
}
}
}
export const tracker: Tracker = Tracker.getInstance(app || remote.app);
+78 -32
View File
@@ -1,59 +1,105 @@
import ElectronStore from "electron-store"
import { app, remote } from "electron"
import { computed, observable, reaction, toJS } from "mobx";
import Config from "conf"
import semver from "semver"
import migrations from "../migrations/user-store"
import { Singleton } from "./utils/singleton";
import Singleton from "./utils/singleton";
import { getAppVersion } from "./utils/app-version";
import { tracker } from "./tracker";
export interface UserStoreModel {
lastSeenAppVersion: string;
seenContexts: string[];
preferences: UserPreferences;
}
export interface UserPreferences {
httpsProxy?: string;
colorTheme?: string;
colorTheme?: string | "dark";
allowUntrustedCAs?: boolean;
allowTelemetry?: boolean;
downloadMirror?: string;
downloadMirror?: string | "default";
}
export class UserStore extends Singleton {
protected store = new ElectronStore({
name: "lens-user-store",
migrations: migrations,
});
private storeConfig: Config<UserStoreModel>;
public lastSeenAppVersion() {
return this.store.get('lastSeenAppVersion', "0.0.0")
@observable isReady = false;
@observable lastSeenAppVersion = "0.0.0"
@observable seenContexts = observable.set();
@observable preferences: UserPreferences = {
allowTelemetry: true,
colorTheme: "dark",
downloadMirror: "default",
};
@computed get hasNewAppVersion() {
return semver.gt(getAppVersion(), this.lastSeenAppVersion);
}
public setLastSeenAppVersion(version: string) {
this.store.set('lastSeenAppVersion', version)
private constructor() {
super();
this.init();
}
public getSeenContexts(): Array<string> {
return this.store.get("seenContexts", [])
async init() {
/*await*/ this.load();
this.bindEvents();
this.isReady = true;
}
public storeSeenContext(newContexts: string[]) {
const seenContexts = this.getSeenContexts().concat(newContexts)
// store unique contexts by casting array to set first
const newContextSet = new Set(seenContexts)
const allContexts = [...newContextSet]
this.store.set("seenContexts", allContexts)
return allContexts
saveLastSeenAppVersion() {
this.lastSeenAppVersion = getAppVersion();
}
public setPreferences(preferences: UserPreferences) {
this.store.set('preferences', preferences)
// todo: use "conf" as pseudo-async for more future-proof usages
protected async load() {
this.storeConfig = new Config<UserStoreModel>({
configName: "lens-user-store",
migrations: migrations,
cwd: (app || remote.app).getPath("userData"),
});
this.fromStore(this.storeConfig.store);
}
public getPreferences(): UserPreferences {
const prefs = this.store.get("preferences", {})
if (!prefs.colorTheme) {
prefs.colorTheme = "dark"
protected bindEvents() {
// refresh from file-system updates
this.storeConfig.onDidAnyChange((data, oldValue) => {
this.fromStore(data);
});
// refresh config file from runtime
reaction(() => this.toJSON(), model => {
this.storeConfig.store = model;
});
// track telemetry availability
reaction(() => this.preferences.allowTelemetry, allowed => {
tracker.event("telemetry", allowed ? "enabled" : "disabled");
});
}
// todo: use "serializr" ?
protected fromStore(data: Partial<UserStoreModel> = {}) {
const { lastSeenAppVersion, seenContexts, preferences } = data
if (lastSeenAppVersion) {
this.lastSeenAppVersion = lastSeenAppVersion;
}
if (!prefs.downloadMirror) {
prefs.downloadMirror = "default"
if (seenContexts) {
this.seenContexts = observable.set(seenContexts)
}
if (prefs.allowTelemetry === undefined) {
prefs.allowTelemetry = true
if (preferences) {
Object.assign(this.preferences, preferences);
}
}
return prefs
protected toJSON(): UserStoreModel {
return toJS({
lastSeenAppVersion: this.lastSeenAppVersion,
seenContexts: Array.from(this.seenContexts),
preferences: this.preferences,
})
}
}
+5 -1
View File
@@ -6,7 +6,8 @@
* const usersStore: UsersStore = UsersStore.getInstance();
*/
export class Singleton {
// todo: maybe convert to @decorator
class Singleton {
private static instances = new WeakMap<object, Singleton>();
// todo: figure out how to infer child class + arguments types
@@ -21,3 +22,6 @@ export class Singleton {
Singleton.instances.delete(this);
}
}
export { Singleton }
export default Singleton;
+16 -21
View File
@@ -1,30 +1,26 @@
import ElectronStore from "electron-store"
import { Singleton } from "./utils/singleton";
import Config from "conf"
import Singleton from "./utils/singleton";
import { clusterStore } from "./cluster-store"
import { getAppVersion } from "./utils/app-version";
export type WorkspaceId = string;
export interface WorkspaceData {
export interface WorkspaceStoreModel {
workspaces: Workspace[]
}
export interface Workspace {
id: WorkspaceId;
name: string;
description?: string;
}
export class Workspace implements WorkspaceData {
public id: string
public name: string
public description?: string
public constructor(data: WorkspaceData) {
Object.assign(this, data)
}
}
export class WorkspaceStore extends Singleton {
static defaultId = "default"
static readonly defaultId = "default"
private store = new ElectronStore({
name: "lens-workspace-store"
private storeConfig = new Config<WorkspaceStoreModel>({
configName: "lens-workspace-store",
projectVersion: getAppVersion(),
});
private constructor() {
@@ -46,11 +42,10 @@ export class WorkspaceStore extends Singleton {
}
public getAllWorkspaces(): Workspace[] {
const workspacesData: WorkspaceData[] = this.store.get("workspaces", [])
return workspacesData.map((wsd) => new Workspace(wsd))
return this.storeConfig.get("workspaces", [])
}
public saveWorkspace(workspace: WorkspaceData) {
public saveWorkspace(workspace: Workspace) {
const workspaces = this.getAllWorkspaces()
const index = workspaces.findIndex((w) => w.id === workspace.id)
if (index !== -1) {
@@ -58,7 +53,7 @@ export class WorkspaceStore extends Singleton {
} else {
workspaces.push(workspace)
}
this.store.set("workspaces", workspaces)
this.storeConfig.set("workspaces", workspaces)
}
public removeWorkspace(workspace: Workspace) {
@@ -70,7 +65,7 @@ export class WorkspaceStore extends Singleton {
if (index !== -1) {
clusterStore.removeClustersByWorkspace(workspace.id)
workspaces.splice(index, 1)
this.store.set("workspaces", workspaces)
this.storeConfig.set("workspaces", workspaces)
}
}
}