preferences page -- part 2

Signed-off-by: Roman <ixrock@gmail.com>
This commit is contained in:
Roman committed 2020-07-23 16:01:32 +03:00
1 parent 24f754697b
commit 6fd9b7cf76
14 files changed
+452 -535

No files matched your search

@@ -1,259 +0,0 @@
<template>
<div class="content">
<ClosePageButton />
<div class="container-fluid lens-preferences">
<div class="header sticky-top">
<h2>Preferences</h2>
</div>
<div class="row">
<div class="col-3" />
<div ref="content" class="global-settings col-6">
<h2>Color Theme</h2>
<b-form-group
label="Cluster Color theme:"
label-for="input-color-theme"
>
<b-form-select
id="input-color-theme"
v-model="preferences.colorTheme"
@change="onSave"
:options="colorThemeOptions"
/>
</b-form-group>
</div>
<div class="col-3" />
</div>
<div class="row">
<div class="col-3" />
<div ref="content" class="global-settings col-6">
<h2>Download Mirror</h2>
<b-form-group
label="Download mirror for kubectl:"
label-for="input-download-mirror"
>
<b-form-select
id="input-download-mirror"
v-model="preferences.downloadMirror"
@change="onSave"
:options="downloadMirrorOptions"
/>
</b-form-group>
</div>
<div class="col-3" />
</div>
<div class="row">
<div class="col-3" />
<div ref="content" class="global-settings col-6">
<h2>Helm</h2>
<b-form-group
label="Repositories:"
label-for="textarea-helm-repositories"
>
<b-input-group>
<b-form-select
:options="hubRepositories"
v-model="selectedHubRepo"
>
<template v-slot:first>
<option value="">
Select Helm repository
</option>
</template>
</b-form-select>
<b-input-group-append>
<b-button :disabled="!selectedHubRepo" @click="addHubRepo" variant="primary">
Add <b-spinner small v-if="isHelmProcessing" label="Small Spinner" />
</b-button>
</b-input-group-append>
</b-input-group>
</b-form-group>
<b-list-group>
<b-list-group-item v-for="repo in helmRepos" :key="repo.name">
<span v-b-tooltip.hover v-b-tooltip.right :title="repo.url">{{ repo.name }}</span>
<button @click="removeRepo(repo)" :data-repo="repo.name" class="close">
<i class="material-icons">delete</i>
</button>
</b-list-group-item>
</b-list-group>
</div>
<div class="col-3" />
</div>
<div class="row">
<div class="col-3" />
<div ref="content" class="global-settings col-6">
<h2>HTTP Proxy</h2>
<b-form-group
label="Proxy URL:"
label-for="input-https-proxy"
description="Proxy is used only for non-cluster communication."
>
<b-form-input
id="input-https-proxy"
v-model="preferences.httpsProxy"
trim
@blur="onSave"
placeholder="Type HTTP proxy url (example: http://proxy.acme.org:8080)"
/>
</b-form-group>
<h2>Certificate Trust</h2>
<b-form-group
label-for="checkbox-allow-untrusted-cas"
description="This will make Lens to trust ANY certificate authority without any validations. Needed with some corporate proxies that do certificate re-writing. Does not affect cluster communications!"
>
<b-form-checkbox
id="checkbox-allow-untrusted-cas"
switch
v-model="preferences.allowUntrustedCAs"
@input="onSave"
>
Allow untrusted Certificate Authorities
</b-form-checkbox>
</b-form-group>
<h2>Telemetry & Usage Tracking</h2>
<b-form-group
label-for="checkbox-allow-telemetry"
description="Telemetry & usage data is collected to continuously improve the Lens experience."
>
<b-form-checkbox
id="checkbox-allow-telemetry"
switch
v-model="preferences.allowTelemetry"
@input="onSave"
>
Allow telemetry & usage tracking
</b-form-checkbox>
</b-form-group>
</div>
<div class="col-3" />
</div>
</div>
</div>
</template>
<script>
import request from 'request-promise-native'
import { globalRequestOpts } from "../../../common/request"
import ClosePageButton from "@/_vue/components/common/ClosePageButton";
export default {
name: 'PreferencesPage',
components: {
ClosePageButton
},
data(){
return {
hubRepositories: [],
selectedHubRepo: "",
colorThemeOptions: [
{ value: "dark", text: "Dark" },
{ value: "light", text: "Light" },
],
downloadMirrorOptions: [
{ value: "default", text: "Default (Google)" },
{ value: "china", text: "China (Azure)" },
],
isHelmProcessing: null,
}
},
computed: {
helmRepos: function() {
return this.$store.getters.repos;
},
preferences: function() {
return this.$store.getters.preferences;
},
},
methods: {
onSave: function() {
this.$store.commit("savePreferences", this.preferences);
},
request: async function(opts) {
const o = Object.assign({
timeout: 10000,
headers: {},
resolveWithFullResponse: true,
json: true
}, globalRequestOpts(opts))
return await request(o)
},
repoCompare: function( a, b ) {
if ( a.name < b.name ){
return -1;
}
if ( a.name > b.name ){
return 1;
}
return 0;
},
addHubRepo: async function() {
if(!this.selectedHubRepo || this.selectedHubRepo === "") {
return
}
this.isHelmProcessing = true
const [name, url ] = this.selectedHubRepo.split("|")
await this.$store.dispatch('addHelmRepo', { name, url })
this.selectedHubRepo = ""
this.isHelmProcessing = false
},
removeRepo: async function(repo) {
await this.$store.dispatch('removeHelmRepo', repo)
},
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
}
})
}
},
created() {
this.$store.dispatch('refreshHelmRepos')
},
mounted: async function() {
this.$store.commit("hideMenu");
this.loadHubRepositories()
},
destroyed: function() {
this.$store.commit("showMenu");
}
}
</script>
<style lang="scss" scoped>
#app > .main-view > .content {
left: 70px;
}
.lens-preferences {
height: 100%;
overflow-y: scroll;
& input {
background-color: #252729 !important;
border: 0px !important;
color: #87909c !important;
}
.header {
padding-top: 15px;
}
.close {
text-shadow: none;
}
.list-group-item {
background-color: #252729 !important;
color: #87909c !important;
}
& i {
color: #87909c !important;
}
}
.global-settings {
padding-top: 0.75em;
}
</style>
@@ -1,9 +1,23 @@
.Preferences {
--flex-gap: #{$padding};
h2 {
&:not(:first-child) {
margin-top: $padding * 3;
}
}
.info-block {
--flex-gap: #{$padding};
}
.repos {
--flex-gap: #{$padding};
> .title {
font-style: italic;
}
.Badge {
margin: $padding / 2;
}
}
}
@@ -1,45 +1,121 @@
import "./preferences.scss"
import React, { Fragment } from "react";
import { observer } from "mobx-react";
import { action, computed, observable } from "mobx";
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 { userStore } from "../../../common/user-store";
import { HelmRepo, repoManager } from "../../../main/helm/helm-repo-manager";
import { Input } from "../input";
import { Checkbox } from "../checkbox";
type ThemeSelectOption = SelectOption & { type: ThemeType }
import { Notifications } from "../notifications";
import { Badge } from "../badge";
import { Spinner } from "../spinner";
@observer
export class Preferences extends React.Component {
themeOptions: ThemeSelectOption[] = [
{ value: "kontena-dark", label: <Trans>Dark</Trans>, type: ThemeType.DARK },
{ value: "kontena-light", label: <Trans>Light</Trans>, type: ThemeType.LIGHT },
@observable helmLoading = false;
@observable helmUpdating = false;
@observable helmRepos: HelmRepo[] = [];
@observable helmAddedRepos = observable.map<string, HelmRepo>();
@observable themeOptions: SelectOption<string>[] = [
{ value: "kontena-dark", label: <Trans>Dark</Trans> },
{ value: "kontena-light", label: <Trans>Light</Trans> },
]
downloadMirrorOptions: SelectOption[] = [
@observable downloadMirrorOptions: SelectOption<string>[] = [
{ value: "default", label: "Default (Google)" },
{ value: "china", label: "China (Azure)" },
]
onThemeChange = ({ value }: ThemeSelectOption) => {
@computed get helmOptions(): SelectOption<HelmRepo>[] {
return this.helmRepos.map(repo => ({
value: repo,
label: repo.name,
}))
}
async componentDidMount() {
await this.loadHelmRepos();
}
@action
async loadHelmRepos() {
this.helmLoading = true;
try {
if (!this.helmRepos.length) {
this.helmRepos = await repoManager.loadAvailableRepos(); // via https://helm.sh
}
const repos = await repoManager.repositories(); // via helm-cli
this.helmAddedRepos.clear();
repos.forEach(repo => this.helmAddedRepos.set(repo.name, repo));
} catch (err) {
Notifications.error(err);
}
this.helmLoading = false;
}
async addRepo(repo: HelmRepo) {
try {
await repoManager.addRepo(repo);
this.helmAddedRepos.set(repo.name, repo);
} catch (err) {
Notifications.error(<Trans>Adding helm branch <b>{repo.name}</b> has failed: {String(err)}</Trans>)
}
}
async removeRepo(repo: HelmRepo) {
try {
await repoManager.removeRepo(repo);
this.helmAddedRepos.delete(repo.name);
} catch (err) {
Notifications.error(
<Trans>Removing helm branch <b>{repo.name}</b> has failed: {String(err)}</Trans>
)
}
}
onThemeChange = ({ value }: SelectOption<string>) => {
// themeStore.setTheme(value); // fixme: apply theme on the fly for current view
userStore.preferences.colorTheme = value;
}
onRepoSelect = async ({ value: repo }: SelectOption<HelmRepo>) => {
const isAdded = this.helmAddedRepos.has(repo.name);
if (isAdded) {
Notifications.ok(<Trans>Helm branch <b>{repo.name}</b> already in use</Trans>)
return;
}
this.helmUpdating = false;
await this.addRepo(repo);
this.helmUpdating = false;
}
formatHelmOptionLabel = ({ value: repo }: SelectOption<HelmRepo>) => {
const isAdded = this.helmAddedRepos.has(repo.name);
return (
<div className="flex gaps">
<span>{repo.name}</span>
{isAdded && <Icon small material="check" className="box right"/>}
</div>
)
}
renderInfo() {
return (
<Fragment>
<h2>
<Trans>Preferences</Trans>
</h2>
<div className="info-block flex gaps align-center">
<div className="info-block flex gaps align-flex-start">
<Icon small material="info"/>
<small>
<p>
<Trans>Lens Global Settings</Trans> (<Trans>applicable to all clusters</Trans>)
</small>
</p>
</div>
</Fragment>
)
@@ -58,15 +134,41 @@ export class Preferences extends React.Component {
<h2><Trans>Download Mirror</Trans></h2>
<Select
placeholder={_i18n._(t`Download mirror for kubectl`)}
placeholder={<Trans>Download mirror for kubectl</Trans>}
options={this.downloadMirrorOptions}
value={preferences.downloadMirror}
onChange={({ value }: SelectOption) => preferences.downloadMirror = value}
/>
<h2><Trans>Helm</Trans></h2>
<div className="helm">
// todo: added helm repos
<Select
placeholder={<Trans>Repositories</Trans>}
isLoading={this.helmLoading}
isDisabled={this.helmUpdating}
options={this.helmOptions}
onChange={this.onRepoSelect}
formatOptionLabel={this.formatHelmOptionLabel}
controlShouldRenderValue={false}
/>
<div className="repos flex gaps align-center">
<div className="title">
<Trans>Added repos:</Trans>
</div>
<div className="repos-list">
{this.helmLoading && <Spinner/>}
{Array.from(this.helmAddedRepos).map(([name, repo]) => {
return (
<Badge key={name} className="added-repo flex gaps align-center">
<span className="repo">{name}</span>
<Icon
material="remove_circle_outline"
onClick={() => this.removeRepo(repo)}
tooltip={<Trans>Remove</Trans>}
/>
</Badge>
)
})}
</div>
</div>
<h2><Trans>HTTP Proxy</Trans></h2>
+1 -1
View File
@@ -5,8 +5,8 @@ import { cssNames } from "../../utils/cssNames";
import { TooltipDecoratorProps, withTooltip } from "../tooltip";
interface Props extends React.HTMLAttributes<any>, TooltipDecoratorProps {
label: React.ReactNode;
small?: boolean;
label?: React.ReactNode;
}
@withTooltip
@@ -7,9 +7,13 @@
display: grid;
grid-template-columns: 1fr 40%;
> .content-col {
> * {
@include hidden-scrollbar;
--flex-gap: #{$spacing};
padding: $spacing;
}
> .content-col {
margin-right: $spacing;
background-color: var(--clusters-menu-bgc);
border-radius: $radius;
@@ -22,9 +26,6 @@
}
> .info-col {
@include hidden-scrollbar;
--flex-gap: #{$spacing};
padding: $spacing;
border-left: 1px solid #353a3e;
}
+8 -114
View File
@@ -1,126 +1,20 @@
import { action, autorun, computed, observable, reaction } from "mobx";
import { autobind, createStorage } from "./utils";
import { Notifications } from "./components/notifications";
interface ITheme {
name: string;
type: "dark" | "light";
author?: string;
colors?: {
[key: string]: string;
blue: string;
magenta: string;
golden: string;
halfGray: string;
primary: string;
textColorPrimary: string;
textColorSecondary: string;
textColorAccent: string;
borderColor: string;
borderFaintColor: string;
mainBackground: string;
contentColor: string;
layoutBackground: string;
layoutTabsBackground: string;
layoutTabsActiveColor: string;
layoutTabsLineColor: string;
sidebarLogoBackground: string;
sidebarActiveColor: string;
sidebarSubmenuActiveColor: string;
sidebarBackground: string;
buttonPrimaryBackground: string;
buttonDefaultBackground: string;
buttonAccentBackground: string;
buttonDisabledBackground: string;
tableBgcStripe: string;
tableBgcSelected: string;
tableHeaderBackground: string;
tableHeaderBorderWidth: string;
tableHeaderBorderColor: string;
tableHeaderColor: string;
tableSelectedRowColor: string;
helmLogoBackground: string;
helmImgBackground: string;
helmStableRepo: string;
helmIncubatorRepo: string;
helmDescriptionHr: string;
helmDescriptionBlockqouteColor: string;
helmDescriptionBlockqouteBorder: string;
helmDescriptionBlockquoteBackground: string;
helmDescriptionHeaders: string;
helmDescriptionH6: string;
helmDescriptionTdBorder: string;
helmDescriptionTrBackground: string;
helmDescriptionCodeBackground: string;
helmDescriptionPreBackground: string;
helmDescriptionPreColor: string;
colorSuccess: string;
colorOk: string;
colorInfo: string;
colorError: string;
colorSoftError: string;
colorWarning: string;
colorVague: string;
colorTerminated: string;
dockHeadBackground: string;
dockInfoBackground: string;
dockInfoBorderColor: string;
terminalBackground: string;
terminalForeground: string;
terminalCursor: string;
terminalCursorAccent: string;
terminalSelection: string;
terminalBlack: string;
terminalRed: string;
terminalGreen: string;
terminalYellow: string;
terminalBlue: string;
terminalMagenta: string;
terminalCyan: string;
terminalWhite: string;
terminalBrightBlack: string;
terminalBrightRed: string;
terminalBrightGreen: string;
terminalBrightYellow: string;
terminalBrightBlue: string;
terminalBrightMagenta: string;
terminalBrightCyan: string;
terminalBrightWhite: string;
dialogTextColor: string;
dialogBackground: string;
dialogHeaderBackground: string;
dialogFooterBackground: string;
drawerTogglerBackground: string;
drawerTitleText: string;
drawerSubtitleBackground: string;
drawerItemNameColor: string;
drawerItemValueColor: string;
boxShadow: string;
iconActiveColor: string;
iconActiveBackground: string;
filterAreaBackground: string;
chartLiveBarBackgound: string;
chartStripesColor: string;
chartCapacityColor: string;
pieChartDefaultColor: string;
selectOptionHoveredColor: string;
lineProgressBackground: string;
radioActiveBackground: string;
};
}
import { Theme, ThemeType } from "../common/user-store";
@autobind()
export class ThemeStore {
protected style = document.createElement("style");
readonly defaultTheme: ITheme = {
readonly defaultTheme: Theme = {
name: "kontena-dark",
type: "dark",
colors: {} as any,
type: ThemeType.DARK,
colors: {},
};
@observable activeThemeId = this.defaultTheme.name; // theme's filename without extension
@observable themes = observable.map<string, ITheme>([], { deep: false });
@observable themes = observable.map<string, Theme>([], { deep: false });
@computed get activeTheme() {
return this.themes.get(this.activeThemeId) || this.defaultTheme;
@@ -157,7 +51,7 @@ export class ThemeStore {
})
}
protected onChange = (theme: ITheme) => {
protected onChange = (theme: Theme) => {
let cssText = "\n"
Object.entries(theme.colors).forEach(([propName, color]) => {
cssText += `--${propName}: ${color} !important;\n`
@@ -165,12 +59,12 @@ export class ThemeStore {
this.style.textContent = `:root {${cssText}} `;
}
async load(themeId: string, { showErrorNotification = true } = {}): Promise<ITheme> {
async load(themeId: string, { showErrorNotification = true } = {}): Promise<Theme> {
if (this.themes.has(themeId)) {
return this.themes.get(themeId);
}
try {
const theme: ITheme = require( // eslint-disable-line @typescript-eslint/no-var-requires
const theme: Theme = require( // eslint-disable-line @typescript-eslint/no-var-requires
/* webpackMode: "lazy", webpackChunkName: "theme/[request]" */
`./themes/${themeId}.json`
);