preferences page -- part 1

Signed-off-by: Roman <ixrock@gmail.com>
This commit is contained in:
Roman committed 2020-07-22 19:21:46 +03:00
1 parent a4bcfc1e52
commit 24f754697b
13 files changed
+381 -60

No files matched your search

+9 -2
View File
@@ -5,9 +5,14 @@ import migrations from "../migrations/user-store"
import { getAppVersion } from "./utils/app-version";
import { tracker } from "./tracker";
// todo: merge with common/user-store.ts
// todo: merge with config.store.ts + theme.store.ts
// fixme: detect new contexts from .kube/config since last open
export enum ThemeType {
LIGHT = "light",
DARK = "dark",
}
export interface UserStoreModel {
lastSeenAppVersion: string;
seenContexts: string[];
@@ -16,7 +21,7 @@ export interface UserStoreModel {
export interface UserPreferences {
httpsProxy?: string;
colorTheme?: string | "dark";
colorTheme?: string | "dark" | "light";
allowUntrustedCAs?: boolean;
allowTelemetry?: boolean;
downloadMirror?: string | "default";
@@ -41,8 +46,10 @@ export class UserStore extends BaseStore<UserStoreModel> {
@observable preferences: UserPreferences = {
allowTelemetry: true,
allowUntrustedCAs: false,
colorTheme: "dark",
downloadMirror: "default",
httpsProxy: "",
};
get isNewVersion() {
+3 -44
View File
@@ -17,12 +17,13 @@ export type HelmRepo = {
}
export class HelmRepoManager extends Singleton {
public static cache = {}
static cache = {}
protected helmEnv: HelmEnv
protected initialized: boolean
public async init() {
const helm = await helmCli.binaryPath()
await helmCli.ensureBinary();
if (!this.initialized) {
this.helmEnv = await this.parseHelmEnv()
await this.update()
@@ -95,38 +96,6 @@ export class HelmRepoManager extends Singleton {
return stdout
}
protected async addRepositories(repositories: HelmRepo[]) {
const currentRepositories = await this.repositories()
repositories.forEach(async (repo: HelmRepo) => {
try {
const repoExists = currentRepositories.find((currentRepo: HelmRepo) => {
return currentRepo.url == repo.url
})
if (!repoExists) {
await this.addRepo(repo)
}
} catch (error) {
logger.error(JSON.stringify(error))
}
});
}
protected async pruneRepositories(repositoriesToKeep: HelmRepo[]) {
const repositories = await this.repositories()
repositories.filter((repo: HelmRepo) => {
return repositoriesToKeep.find((repoToKeep: HelmRepo) => {
return repo.name == repoToKeep.name
}) === undefined
}).forEach(async (repo: HelmRepo) => {
try {
const output = await this.removeRepo(repo)
logger.debug(output)
} catch (error) {
logger.error(error)
}
})
}
public async addRepo(repository: HelmRepo) {
const helm = await helmCli.binaryPath()
logger.debug(`${helm} repo add ${repository.name} ${repository.url}`)
@@ -136,16 +105,6 @@ export class HelmRepoManager extends Singleton {
})
return stdout
}
public async removeRepo(repository: HelmRepo): Promise<string> {
const helm = await helmCli.binaryPath()
logger.debug(`${helm} repo remove ${repository.name} ${repository.url}`)
const { stdout, stderr } = await promiseExec(`"${helm}" repo remove ${repository.name}`).catch((error) => {
throw(error.stderr)
})
return stdout
}
}
export const repoManager = HelmRepoManager.getInstance<HelmRepoManager>()
+1 -1
View File
@@ -94,7 +94,7 @@ class ConfigRoute extends LensApi {
const data: IConfigRoutePayload = {
clusterName: cluster.contextName,
lensVersion: app.getVersion(),
lensTheme: `kontena-${userStore.preferences.colorTheme}`,
lensTheme: userStore.preferences.colorTheme,
kubeVersion: cluster.version,
chartsEnabled: true,
isClusterAdmin: cluster.isAdmin,
@@ -206,7 +206,10 @@ export default {
loadHubRepositories: async function() {
const res = await this.request({ uri: "https://hub.helm.sh/assets/js/repos.json"}).catch((error) => { this.hubRepositories = [] })
this.hubRepositories = res.body.data.sort(this.repoCompare).map((repo) => {
return { text: repo.name, value: repo.name+"|"+repo.url}
return {
text: repo.name,
value: repo.name+"|"+repo.url
}
})
}
},
@@ -1,3 +1,9 @@
.Preferences {
--flex-gap: #{$padding};
h2 {
&:not(:first-child) {
margin-top: $padding * 3;
}
}
}
@@ -1,14 +1,106 @@
import "./preferences.scss"
import React from "react";
import React, { Fragment } from "react";
import { observer } from "mobx-react";
import { t, Trans } from "@lingui/macro";
import { _i18n } from "../../i18n";
import { WizardLayout } from "../layout/wizard-layout";
import { Icon } from "../icon";
import { Select, SelectOption } from "../select";
import { ThemeType, userStore } from "../../../common/user-store";
import { Input } from "../input";
import { Checkbox } from "../checkbox";
type ThemeSelectOption = SelectOption & { type: ThemeType }
@observer
export class Preferences extends React.Component {
render() {
themeOptions: ThemeSelectOption[] = [
{ value: "kontena-dark", label: <Trans>Dark</Trans>, type: ThemeType.DARK },
{ value: "kontena-light", label: <Trans>Light</Trans>, type: ThemeType.LIGHT },
]
downloadMirrorOptions: SelectOption[] = [
{ value: "default", label: "Default (Google)" },
{ value: "china", label: "China (Azure)" },
]
onThemeChange = ({ value }: ThemeSelectOption) => {
// themeStore.setTheme(value); // fixme: apply theme on the fly for current view
userStore.preferences.colorTheme = value;
}
renderInfo() {
return (
<div className="Preferences">
Preferences
</div>
<Fragment>
<h2>
<Trans>Preferences</Trans>
</h2>
<div className="info-block flex gaps align-center">
<Icon small material="info"/>
<small>
<Trans>Lens Global Settings</Trans> (<Trans>applicable to all clusters</Trans>)
</small>
</div>
</Fragment>
)
}
render() {
const { preferences } = userStore;
return (
<WizardLayout className="Preferences" infoPanel={this.renderInfo()}>
<h2><Trans>Color Theme</Trans></h2>
<Select
options={this.themeOptions}
value={preferences.colorTheme}
onChange={this.onThemeChange}
/>
<h2><Trans>Download Mirror</Trans></h2>
<Select
placeholder={_i18n._(t`Download mirror for kubectl`)}
options={this.downloadMirrorOptions}
value={preferences.downloadMirror}
onChange={({ value }: SelectOption) => preferences.downloadMirror = value}
/>
<h2><Trans>Helm</Trans></h2>
<div className="helm">
// todo: added helm repos
</div>
<h2><Trans>HTTP Proxy</Trans></h2>
<Input
placeholder={_i18n._(t`Type HTTP proxy url (example: http://proxy.acme.org:8080)`)}
value={preferences.httpsProxy}
onChange={v => preferences.httpsProxy = v}
/>
<small className="hint">
<Trans>Proxy is used only for non-cluster communication.</Trans>
</small>
<h2><Trans>Certificate Trust</Trans></h2>
<Checkbox
label={<Trans>Allow untrusted Certificate Authorities</Trans>}
value={preferences.allowUntrustedCAs}
onChange={v => preferences.allowUntrustedCAs = v}
/>
<small className="hint">
<Trans>This will make Lens to trust ANY certificate authority without any validations.</Trans>{" "}
<Trans>Needed with some corporate proxies that do certificate re-writing.</Trans>{" "}
<Trans>Does not affect cluster communications!</Trans>
</small>
<h2><Trans>Telemetry & Usage Tracking</Trans></h2>
<Checkbox
label={<Trans>Allow telemetry & usage tracking</Trans>}
value={preferences.allowTelemetry}
onChange={v => preferences.allowTelemetry = v}
/>
<small className="hint">
<Trans>Telemetry & usage data is collected to continuously improve the Lens experience.</Trans>
</small>
</WizardLayout>
)
}
}
+2
View File
@@ -37,6 +37,7 @@ import { clusterIpc } from "../../common/cluster-ipc";
import { clusterStore } from "../../common/cluster-store";
import { ClusterStatus } from "./cluster-manager/cluster-status";
import { clusterStatusRoute, clusterStatusURL } from "./cluster-manager/cluster-status.route";
import { Preferences, preferencesRoute } from "./+preferences";
import { navigation } from "../navigation";
import { CubeSpinner } from "./spinner";
@@ -85,6 +86,7 @@ export class App extends React.Component {
<ErrorBoundary>
<Switch>
<Route component={LandingPage} {...landingRoute}/>
<Route component={Preferences} {...preferencesRoute}/>
<Route component={Workspaces} {...workspacesRoute}/>
<Route component={AddCluster} {...addClusterRoute}/>
<Route component={Cluster} {...clusterRoute}/>
@@ -1,5 +1,5 @@
.WizardLayout {
--flex-gap: #{$padding * 2};
$spacing: $padding * 2;
position: relative;
padding: $padding * 3;
@@ -8,8 +8,9 @@
grid-template-columns: 1fr 40%;
> .content-col {
padding: var(--flex-gap);
margin-right: var(--flex-gap);
--flex-gap: #{$spacing};
padding: $spacing;
margin-right: $spacing;
background-color: var(--clusters-menu-bgc);
border-radius: $radius;
@@ -22,7 +23,8 @@
> .info-col {
@include hidden-scrollbar;
padding: var(--flex-gap);
--flex-gap: #{$spacing};
padding: $spacing;
border-left: 1px solid #353a3e;
}
+1 -1
View File
@@ -7,7 +7,7 @@ import { createPortal } from "react-dom"
import { autobind, cssNames } from "../../utils";
import { Animate } from "../animate";
// todo: refactor -- better positioning + remove "flying effect"
// fixme: refactor -- better positioning + remove "flying effect"
// todo: always render outside of parent element ("overflow: auto" should not affect tooltip)
export interface TooltipProps {
-2
View File
@@ -13,7 +13,6 @@ import { _i18n } from "./i18n";
import { ClusterManager } from "./components/cluster-manager";
import { ErrorBoundary } from "./components/error-boundary";
import { WhatsNew, whatsNewRoute } from "./components/+whats-new";
import { Preferences, preferencesRoute } from "./components/+preferences";
@observer
class LensApp extends React.Component {
@@ -37,7 +36,6 @@ class LensApp extends React.Component {
<Switch>
{userStore.isNewVersion && <Route component={WhatsNew}/>}
<Route component={WhatsNew} {...whatsNewRoute}/>
<Route component={Preferences} {...preferencesRoute}/>
<Route component={ClusterManager}/>
</Switch>
</ErrorBoundary>