mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Merge branch 'lensapp:master' into feature/port-forward-custom-port
This commit is contained in:
commit
8ebad5a071
11
.github/actions/add-card-to-project/Dockerfile
vendored
Normal file
11
.github/actions/add-card-to-project/Dockerfile
vendored
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
# Container image that runs your code
|
||||||
|
FROM alpine:3.10
|
||||||
|
|
||||||
|
RUN apk add --no-cache --no-progress curl jq
|
||||||
|
|
||||||
|
# Copies your code file from your action repository to the filesystem path `/` of the container
|
||||||
|
COPY entrypoint.sh /entrypoint.sh
|
||||||
|
RUN chmod +x /entrypoint.sh
|
||||||
|
|
||||||
|
# Code file to execute when the docker container starts up (`entrypoint.sh`)
|
||||||
|
ENTRYPOINT ["/entrypoint.sh"]
|
||||||
23
.github/actions/add-card-to-project/action.yml
vendored
Normal file
23
.github/actions/add-card-to-project/action.yml
vendored
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
name: 'add_card_to_project'
|
||||||
|
description: 'A GitHub Action to add a card to a project and set the card position'
|
||||||
|
author: 'Steve Richards'
|
||||||
|
branding:
|
||||||
|
icon: 'command'
|
||||||
|
color: 'blue'
|
||||||
|
inputs:
|
||||||
|
project:
|
||||||
|
description: 'The url of the project to be assigned to.'
|
||||||
|
required: true
|
||||||
|
column_name:
|
||||||
|
description: 'The column name of the project, defaults to "To do" for issues and "In progress" for pull requests.'
|
||||||
|
required: false
|
||||||
|
card_position:
|
||||||
|
description: 'The card position of the card in the column, defaults to "bottom". Valid values are "top", "bottom" and "after:<card id>".'
|
||||||
|
required: false
|
||||||
|
runs:
|
||||||
|
using: 'docker'
|
||||||
|
image: 'Dockerfile'
|
||||||
|
args:
|
||||||
|
- ${{ inputs.project }}
|
||||||
|
- ${{ inputs.column_name }}
|
||||||
|
- ${{ inputs.card_position }}
|
||||||
157
.github/actions/add-card-to-project/entrypoint.sh
vendored
Normal file
157
.github/actions/add-card-to-project/entrypoint.sh
vendored
Normal file
@ -0,0 +1,157 @@
|
|||||||
|
#!/bin/sh -l
|
||||||
|
|
||||||
|
PROJECT_URL="$INPUT_PROJECT"
|
||||||
|
if [ -z "$PROJECT_URL" ]; then
|
||||||
|
echo "PROJECT_URL is not defined." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
get_project_type() {
|
||||||
|
_PROJECT_URL="$1"
|
||||||
|
|
||||||
|
case "$_PROJECT_URL" in
|
||||||
|
https://github.com/orgs/*)
|
||||||
|
echo "org"
|
||||||
|
;;
|
||||||
|
https://github.com/users/*)
|
||||||
|
echo "user"
|
||||||
|
;;
|
||||||
|
https://github.com/*/projects/*)
|
||||||
|
echo "repo"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Invalid PROJECT_URL: $_PROJECT_URL" >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
unset _PROJECT_URL
|
||||||
|
}
|
||||||
|
|
||||||
|
find_project_id() {
|
||||||
|
_PROJECT_TYPE="$1"
|
||||||
|
_PROJECT_URL="$2"
|
||||||
|
|
||||||
|
case "$_PROJECT_TYPE" in
|
||||||
|
org)
|
||||||
|
_ORG_NAME=$(echo "$_PROJECT_URL" | sed -e 's@https://github.com/orgs/\([^/]\+\)/projects/[0-9]\+@\1@')
|
||||||
|
_ENDPOINT="https://api.github.com/orgs/$_ORG_NAME/projects"
|
||||||
|
;;
|
||||||
|
user)
|
||||||
|
_USER_NAME=$(echo "$_PROJECT_URL" | sed -e 's@https://github.com/users/\([^/]\+\)/projects/[0-9]\+@\1@')
|
||||||
|
_ENDPOINT="https://api.github.com/users/$_USER_NAME/projects"
|
||||||
|
;;
|
||||||
|
repo)
|
||||||
|
_ENDPOINT="https://api.github.com/repos/$GITHUB_REPOSITORY/projects"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
_PROJECTS=$(curl -s -X GET -u "$GITHUB_ACTOR:$TOKEN" --retry 3 \
|
||||||
|
-H 'Accept: application/vnd.github.inertia-preview+json' \
|
||||||
|
"$_ENDPOINT")
|
||||||
|
|
||||||
|
_PROJECTID=$(echo "$_PROJECTS" | jq -r ".[] | select(.html_url == \"$_PROJECT_URL\").id")
|
||||||
|
|
||||||
|
if [ "$_PROJECTID" != "" ]; then
|
||||||
|
echo "$_PROJECTID"
|
||||||
|
else
|
||||||
|
echo "No project was found." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
unset _PROJECT_TYPE _PROJECT_URL _ORG_NAME _USER_NAME _ENDPOINT _PROJECTS _PROJECTID
|
||||||
|
}
|
||||||
|
|
||||||
|
find_column_id() {
|
||||||
|
_PROJECT_ID="$1"
|
||||||
|
_INITIAL_COLUMN_NAME="$2"
|
||||||
|
|
||||||
|
_COLUMNS=$(curl -s -X GET -u "$GITHUB_ACTOR:$TOKEN" --retry 3 \
|
||||||
|
-H 'Accept: application/vnd.github.inertia-preview+json' \
|
||||||
|
"https://api.github.com/projects/$_PROJECT_ID/columns")
|
||||||
|
|
||||||
|
|
||||||
|
echo "$_COLUMNS" | jq -r ".[] | select(.name == \"$_INITIAL_COLUMN_NAME\").id"
|
||||||
|
unset _PROJECT_ID _INITIAL_COLUMN_NAME _COLUMNS
|
||||||
|
}
|
||||||
|
|
||||||
|
PROJECT_TYPE=$(get_project_type "${PROJECT_URL:?<Error> required this environment variable}")
|
||||||
|
|
||||||
|
if [ "$PROJECT_TYPE" = org ] || [ "$PROJECT_TYPE" = user ]; then
|
||||||
|
if [ -z "$MY_GITHUB_TOKEN" ]; then
|
||||||
|
echo "MY_GITHUB_TOKEN not defined" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
TOKEN="$MY_GITHUB_TOKEN" # It's User's personal access token. It should be secret.
|
||||||
|
else
|
||||||
|
if [ -z "$GITHUB_TOKEN" ]; then
|
||||||
|
echo "GITHUB_TOKEN not defined" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
TOKEN="$GITHUB_TOKEN" # GitHub sets. The scope in only the repository containing the workflow file.
|
||||||
|
fi
|
||||||
|
|
||||||
|
INITIAL_COLUMN_NAME="$INPUT_COLUMN_NAME"
|
||||||
|
if [ -z "$INITIAL_COLUMN_NAME" ]; then
|
||||||
|
# assing the column name by default
|
||||||
|
INITIAL_COLUMN_NAME='To do'
|
||||||
|
if [ "$GITHUB_EVENT_NAME" == "pull_request" ] || [ "$GITHUB_EVENT_NAME" == "pull_request_target" ]; then
|
||||||
|
echo "changing col name for PR event"
|
||||||
|
INITIAL_COLUMN_NAME='In progress'
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
PROJECT_ID=$(find_project_id "$PROJECT_TYPE" "$PROJECT_URL")
|
||||||
|
INITIAL_COLUMN_ID=$(find_column_id "$PROJECT_ID" "${INITIAL_COLUMN_NAME:?<Error> required this environment variable}")
|
||||||
|
|
||||||
|
if [ -z "$INITIAL_COLUMN_ID" ]; then
|
||||||
|
echo "INITIAL_COLUMN_ID is not found." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
case "$GITHUB_EVENT_NAME" in
|
||||||
|
issues)
|
||||||
|
ISSUE_ID=$(jq -r '.issue.id' < "$GITHUB_EVENT_PATH")
|
||||||
|
|
||||||
|
# Add this issue to the project column
|
||||||
|
_CARDS=$(curl -s -X POST -u "$GITHUB_ACTOR:$TOKEN" --retry 3 \
|
||||||
|
-H 'Accept: application/vnd.github.inertia-preview+json' \
|
||||||
|
-d "{\"content_type\": \"Issue\", \"content_id\": $ISSUE_ID}" \
|
||||||
|
"https://api.github.com/projects/columns/$INITIAL_COLUMN_ID/cards")
|
||||||
|
;;
|
||||||
|
pull_request|pull_request_target)
|
||||||
|
PULL_REQUEST_ID=$(jq -r '.pull_request.id' < "$GITHUB_EVENT_PATH")
|
||||||
|
|
||||||
|
# Add this pull_request to the project column
|
||||||
|
_CARDS=$(curl -s -X POST -u "$GITHUB_ACTOR:$TOKEN" --retry 3 \
|
||||||
|
-H 'Accept: application/vnd.github.inertia-preview+json' \
|
||||||
|
-d "{\"content_type\": \"PullRequest\", \"content_id\": $PULL_REQUEST_ID}" \
|
||||||
|
"https://api.github.com/projects/columns/$INITIAL_COLUMN_ID/cards")
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Nothing to be done on this action: $GITHUB_EVENT_NAME" >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
CARDS_ID=$(echo "$_CARDS" | jq -r .id)
|
||||||
|
unset _CARDS
|
||||||
|
|
||||||
|
if [ -z "$CARDS_ID" ]; then
|
||||||
|
echo "CARDS_ID is not found." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
INITIAL_CARD_POSITION="$INPUT_CARD_POSITION"
|
||||||
|
if [ -z "$INITIAL_CARD_POSITION" ]; then
|
||||||
|
# assign the card position by default
|
||||||
|
INITIAL_CARD_POSITION='bottom'
|
||||||
|
fi
|
||||||
|
|
||||||
|
_CARDS=$(curl -s -X POST -u "$GITHUB_ACTOR:$TOKEN" --retry 3 \
|
||||||
|
-H 'Accept: application/vnd.github.inertia-preview+json' \
|
||||||
|
-d "{\"position\": \"$INITIAL_CARD_POSITION\"}" \
|
||||||
|
"https://api.github.com/projects/columns/cards/$CARDS_ID/moves")
|
||||||
33
.github/workflows/add-to-project-board.yaml
vendored
Normal file
33
.github/workflows/add-to-project-board.yaml
vendored
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
name: Add Card to Project(s)
|
||||||
|
on:
|
||||||
|
issues:
|
||||||
|
types: [opened]
|
||||||
|
pull_request_target:
|
||||||
|
types: [opened]
|
||||||
|
env:
|
||||||
|
MY_GITHUB_TOKEN: ${{ secrets.PROJECT_GITHUB_TOKEN }}
|
||||||
|
EVENT_TYPE: $GITHUB_EVENT_NAME
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
add_card_to_project:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
name: Add Card to Project(s)
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
- name: Get Event Type
|
||||||
|
run: echo $GITHUB_EVENT_NAME
|
||||||
|
- name: Assign NEW issues to project 1
|
||||||
|
uses: ./.github/actions/add-card-to-project
|
||||||
|
if: github.event_name == 'issues' && github.event.action == 'opened'
|
||||||
|
with:
|
||||||
|
project: 'https://github.com/orgs/lensapp/projects/1'
|
||||||
|
column_name: 'Backlog'
|
||||||
|
card_position: 'bottom'
|
||||||
|
- name: Assign NEW pull requests to project 1
|
||||||
|
uses: ./.github/actions/add-card-to-project
|
||||||
|
if: github.event_name == 'pull_request_target' && github.event.action == 'opened'
|
||||||
|
with:
|
||||||
|
project: 'https://github.com/orgs/lensapp/projects/1'
|
||||||
|
column_name: 'PRs'
|
||||||
|
card_position: 'bottom'
|
||||||
13
.github/workflows/test.yml
vendored
13
.github/workflows/test.yml
vendored
@ -77,15 +77,12 @@ jobs:
|
|||||||
sudo chown -R $USER $HOME/.kube $HOME/.minikube
|
sudo chown -R $USER $HOME/.kube $HOME/.minikube
|
||||||
name: Install integration test dependencies
|
name: Install integration test dependencies
|
||||||
if: runner.os == 'Linux'
|
if: runner.os == 'Linux'
|
||||||
- run: xvfb-run --auto-servernum --server-args='-screen 0, 1600x900x24' make integration-linux
|
|
||||||
|
- run: xvfb-run --auto-servernum --server-args='-screen 0, 1600x900x24' make integration
|
||||||
name: Run Linux integration tests
|
name: Run Linux integration tests
|
||||||
if: runner.os == 'Linux'
|
if: runner.os == 'Linux'
|
||||||
|
|
||||||
- run: make integration-win
|
- run: make integration
|
||||||
name: Run Windows integration tests
|
name: Run integration tests
|
||||||
shell: bash
|
shell: bash
|
||||||
if: runner.os == 'Windows'
|
if: runner.os != 'Linux'
|
||||||
|
|
||||||
- run: make integration-mac
|
|
||||||
name: Run macOS integration tests
|
|
||||||
if: runner.os == 'macOS'
|
|
||||||
|
|||||||
17
Makefile
17
Makefile
@ -50,21 +50,8 @@ tag-release:
|
|||||||
test: binaries/client
|
test: binaries/client
|
||||||
yarn run jest $(or $(CMD_ARGS), "src")
|
yarn run jest $(or $(CMD_ARGS), "src")
|
||||||
|
|
||||||
.PHONY: integration-linux
|
.PHONY: integration
|
||||||
integration-linux: binaries/client build-extension-types build-extensions
|
integration: build
|
||||||
yarn build:linux
|
|
||||||
yarn integration
|
|
||||||
|
|
||||||
.PHONY: integration-mac
|
|
||||||
integration-mac: binaries/client build-extension-types build-extensions
|
|
||||||
# rm ${HOME}/Library/Application\ Support/Lens
|
|
||||||
yarn build:mac
|
|
||||||
yarn integration
|
|
||||||
|
|
||||||
.PHONY: integration-win
|
|
||||||
integration-win: binaries/client build-extension-types build-extensions
|
|
||||||
# rm %APPDATA%/Lens
|
|
||||||
yarn build:win
|
|
||||||
yarn integration
|
yarn integration
|
||||||
|
|
||||||
.PHONY: build
|
.PHONY: build
|
||||||
|
|||||||
@ -8,7 +8,8 @@
|
|||||||
"copyright": "© 2021 OpenLens Authors",
|
"copyright": "© 2021 OpenLens Authors",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "OpenLens Authors"
|
"name": "OpenLens Authors",
|
||||||
|
"email": "info@k8slens.dev"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "concurrently -i -k \"yarn run dev-run -C\" yarn:dev:*",
|
"dev": "concurrently -i -k \"yarn run dev-run -C\" yarn:dev:*",
|
||||||
@ -315,7 +316,7 @@
|
|||||||
"concurrently": "^5.2.0",
|
"concurrently": "^5.2.0",
|
||||||
"css-loader": "^5.2.6",
|
"css-loader": "^5.2.6",
|
||||||
"deepdash": "^5.3.5",
|
"deepdash": "^5.3.5",
|
||||||
"dompurify": "^2.0.11",
|
"dompurify": "^2.0.17",
|
||||||
"electron": "^9.4.4",
|
"electron": "^9.4.4",
|
||||||
"electron-builder": "^22.10.5",
|
"electron-builder": "^22.10.5",
|
||||||
"electron-notarize": "^0.3.0",
|
"electron-notarize": "^0.3.0",
|
||||||
|
|||||||
@ -41,6 +41,7 @@ import { SemVer } from "semver";
|
|||||||
import electron from "electron";
|
import electron from "electron";
|
||||||
import { stdout, stderr } from "process";
|
import { stdout, stderr } from "process";
|
||||||
import { beforeEachWrapped } from "../../../integration/helpers/utils";
|
import { beforeEachWrapped } from "../../../integration/helpers/utils";
|
||||||
|
import { ThemeStore } from "../../renderer/theme.store";
|
||||||
|
|
||||||
console = new Console(stdout, stderr);
|
console = new Console(stdout, stderr);
|
||||||
|
|
||||||
@ -72,7 +73,7 @@ describe("user store tests", () => {
|
|||||||
us.httpsProxy = "abcd://defg";
|
us.httpsProxy = "abcd://defg";
|
||||||
|
|
||||||
expect(us.httpsProxy).toBe("abcd://defg");
|
expect(us.httpsProxy).toBe("abcd://defg");
|
||||||
expect(us.colorTheme).toBe(UserStore.defaultTheme);
|
expect(us.colorTheme).toBe(ThemeStore.defaultTheme);
|
||||||
|
|
||||||
us.colorTheme = "light";
|
us.colorTheme = "light";
|
||||||
expect(us.colorTheme).toBe("light");
|
expect(us.colorTheme).toBe("light");
|
||||||
@ -83,7 +84,7 @@ describe("user store tests", () => {
|
|||||||
|
|
||||||
us.colorTheme = "some other theme";
|
us.colorTheme = "some other theme";
|
||||||
us.resetTheme();
|
us.resetTheme();
|
||||||
expect(us.colorTheme).toBe(UserStore.defaultTheme);
|
expect(us.colorTheme).toBe(ThemeStore.defaultTheme);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("correctly calculates if the last seen version is an old release", () => {
|
it("correctly calculates if the last seen version is an old release", () => {
|
||||||
|
|||||||
@ -56,9 +56,15 @@ export abstract class BaseStore<T> extends Singleton {
|
|||||||
cwd: this.cwd(),
|
cwd: this.cwd(),
|
||||||
});
|
});
|
||||||
|
|
||||||
logger.info(`[STORE]: LOADED from ${this.path}`);
|
const res: any = this.fromStore(this.storeConfig.store);
|
||||||
this.fromStore(this.storeConfig.store);
|
|
||||||
|
if (res instanceof Promise || (typeof res === "object" && res && typeof res.then === "function")) {
|
||||||
|
console.error(`${this.name} extends BaseStore<T>'s fromStore method returns a Promise or promise-like object. This is an error and must be fixed.`);
|
||||||
|
}
|
||||||
|
|
||||||
this.enableSync();
|
this.enableSync();
|
||||||
|
|
||||||
|
logger.info(`[STORE]: LOADED from ${this.path}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
get name() {
|
get name() {
|
||||||
@ -157,6 +163,9 @@ export abstract class BaseStore<T> extends Singleton {
|
|||||||
/**
|
/**
|
||||||
* fromStore is called internally when a child class syncs with the file
|
* fromStore is called internally when a child class syncs with the file
|
||||||
* system.
|
* system.
|
||||||
|
*
|
||||||
|
* Note: This function **must** be synchronous.
|
||||||
|
*
|
||||||
* @param data the parsed information read from the stored JSON file
|
* @param data the parsed information read from the stored JSON file
|
||||||
*/
|
*/
|
||||||
protected abstract fromStore(data: T): void;
|
protected abstract fromStore(data: T): void;
|
||||||
|
|||||||
@ -94,7 +94,7 @@ export class HotbarStore extends BaseStore<HotbarStoreModel> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
protected async fromStore(data: Partial<HotbarStoreModel> = {}) {
|
protected fromStore(data: Partial<HotbarStoreModel> = {}) {
|
||||||
if (!data.hotbars || !data.hotbars.length) {
|
if (!data.hotbars || !data.hotbars.length) {
|
||||||
this.hotbars = [{
|
this.hotbars = [{
|
||||||
id: uuid.v4(),
|
id: uuid.v4(),
|
||||||
|
|||||||
23
src/common/user-store/index.ts
Normal file
23
src/common/user-store/index.ts
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2021 OpenLens Authors
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
* this software and associated documentation files (the "Software"), to deal in
|
||||||
|
* the Software without restriction, including without limitation the rights to
|
||||||
|
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
* subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export * from "./user-store";
|
||||||
|
export type { KubeconfigSyncEntry, KubeconfigSyncValue } from "./preferences-helpers";
|
||||||
237
src/common/user-store/preferences-helpers.ts
Normal file
237
src/common/user-store/preferences-helpers.ts
Normal file
@ -0,0 +1,237 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2021 OpenLens Authors
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
* this software and associated documentation files (the "Software"), to deal in
|
||||||
|
* the Software without restriction, including without limitation the rights to
|
||||||
|
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
* subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import moment from "moment-timezone";
|
||||||
|
import path from "path";
|
||||||
|
import os from "os";
|
||||||
|
import { ThemeStore } from "../../renderer/theme.store";
|
||||||
|
import { ObservableToggleSet } from "../utils";
|
||||||
|
|
||||||
|
export interface KubeconfigSyncEntry extends KubeconfigSyncValue {
|
||||||
|
filePath: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface KubeconfigSyncValue { }
|
||||||
|
|
||||||
|
interface PreferenceDescription<T, R = T> {
|
||||||
|
fromStore(val: T | undefined): R;
|
||||||
|
toStore(val: R): T | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const httpsProxy: PreferenceDescription<string | undefined> = {
|
||||||
|
fromStore(val) {
|
||||||
|
return val;
|
||||||
|
},
|
||||||
|
toStore(val) {
|
||||||
|
return val || undefined;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const shell: PreferenceDescription<string | undefined> = {
|
||||||
|
fromStore(val) {
|
||||||
|
return val;
|
||||||
|
},
|
||||||
|
toStore(val) {
|
||||||
|
return val || undefined;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const colorTheme: PreferenceDescription<string> = {
|
||||||
|
fromStore(val) {
|
||||||
|
return val || ThemeStore.defaultTheme;
|
||||||
|
},
|
||||||
|
toStore(val) {
|
||||||
|
if (!val || val === ThemeStore.defaultTheme) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
return val;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const localeTimezone: PreferenceDescription<string> = {
|
||||||
|
fromStore(val) {
|
||||||
|
return val || moment.tz.guess(true) || "UTC";
|
||||||
|
},
|
||||||
|
toStore(val) {
|
||||||
|
if (!val || val === moment.tz.guess(true) || val === "UTC") {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
return val;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const allowUntrustedCAs: PreferenceDescription<boolean> = {
|
||||||
|
fromStore(val) {
|
||||||
|
return val ?? false;
|
||||||
|
},
|
||||||
|
toStore(val) {
|
||||||
|
if (!val) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
return val;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const allowTelemetry: PreferenceDescription<boolean> = {
|
||||||
|
fromStore(val) {
|
||||||
|
return val ?? true;
|
||||||
|
},
|
||||||
|
toStore(val) {
|
||||||
|
if (val === true) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
return val;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const downloadMirror: PreferenceDescription<string> = {
|
||||||
|
fromStore(val) {
|
||||||
|
return val ?? "default";
|
||||||
|
},
|
||||||
|
toStore(val) {
|
||||||
|
if (!val || val === "default") {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
return val;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const downloadKubectlBinaries: PreferenceDescription<boolean> = {
|
||||||
|
fromStore(val) {
|
||||||
|
return val ?? true;
|
||||||
|
},
|
||||||
|
toStore(val) {
|
||||||
|
if (val === true) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
return val;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const downloadBinariesPath: PreferenceDescription<string | undefined> = {
|
||||||
|
fromStore(val) {
|
||||||
|
return val;
|
||||||
|
},
|
||||||
|
toStore(val) {
|
||||||
|
if (!val) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
return val;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const kubectlBinariesPath: PreferenceDescription<string | undefined> = {
|
||||||
|
fromStore(val) {
|
||||||
|
return val;
|
||||||
|
},
|
||||||
|
toStore(val) {
|
||||||
|
if (!val) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
return val;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const openAtLogin: PreferenceDescription<boolean> = {
|
||||||
|
fromStore(val) {
|
||||||
|
return val ?? false;
|
||||||
|
},
|
||||||
|
toStore(val) {
|
||||||
|
if (!val) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
return val;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const hiddenTableColumns: PreferenceDescription<[string, string[]][], Map<string, ObservableToggleSet<string>>> = {
|
||||||
|
fromStore(val) {
|
||||||
|
return new Map(
|
||||||
|
(val ?? []).map(([tableId, columnIds]) => [tableId, new ObservableToggleSet(columnIds)])
|
||||||
|
);
|
||||||
|
},
|
||||||
|
toStore(val) {
|
||||||
|
const res: [string, string[]][] = [];
|
||||||
|
|
||||||
|
for (const [table, columnes] of val) {
|
||||||
|
if (columnes.size) {
|
||||||
|
res.push([table, Array.from(columnes)]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.length ? res : undefined;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const mainKubeFolder = path.join(os.homedir(), ".kube");
|
||||||
|
|
||||||
|
const syncKubeconfigEntries: PreferenceDescription<KubeconfigSyncEntry[], Map<string, KubeconfigSyncValue>> = {
|
||||||
|
fromStore(val) {
|
||||||
|
return new Map(
|
||||||
|
val
|
||||||
|
?.map(({ filePath, ...rest }) => [filePath, rest])
|
||||||
|
?? [[mainKubeFolder, {}]]
|
||||||
|
);
|
||||||
|
},
|
||||||
|
toStore(val) {
|
||||||
|
if (val.size === 1 && val.has(mainKubeFolder)) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Array.from(val, ([filePath, rest]) => ({ filePath, ...rest }));
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
type PreferencesModelType<field extends keyof typeof DESCRIPTORS> = typeof DESCRIPTORS[field] extends PreferenceDescription<infer T, any> ? T : never;
|
||||||
|
type UserStoreModelType<field extends keyof typeof DESCRIPTORS> = typeof DESCRIPTORS[field] extends PreferenceDescription<any, infer T> ? T : never;
|
||||||
|
|
||||||
|
export type UserStoreFlatModel = {
|
||||||
|
[field in keyof typeof DESCRIPTORS]: UserStoreModelType<field>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type UserPreferencesModel = {
|
||||||
|
[field in keyof typeof DESCRIPTORS]: PreferencesModelType<field>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const DESCRIPTORS = {
|
||||||
|
httpsProxy,
|
||||||
|
shell,
|
||||||
|
colorTheme,
|
||||||
|
localeTimezone,
|
||||||
|
allowUntrustedCAs,
|
||||||
|
allowTelemetry,
|
||||||
|
downloadMirror,
|
||||||
|
downloadKubectlBinaries,
|
||||||
|
downloadBinariesPath,
|
||||||
|
kubectlBinariesPath,
|
||||||
|
openAtLogin,
|
||||||
|
hiddenTableColumns,
|
||||||
|
syncKubeconfigEntries,
|
||||||
|
};
|
||||||
@ -19,50 +19,25 @@
|
|||||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type { ThemeId } from "../renderer/theme.store";
|
|
||||||
import { app, remote } from "electron";
|
import { app, remote } from "electron";
|
||||||
import semver from "semver";
|
import semver from "semver";
|
||||||
import { action, computed, observable, reaction, makeObservable } from "mobx";
|
import { action, computed, observable, reaction, makeObservable } from "mobx";
|
||||||
import moment from "moment-timezone";
|
import { BaseStore } from "../base-store";
|
||||||
import { BaseStore } from "./base-store";
|
import migrations from "../../migrations/user-store";
|
||||||
import migrations from "../migrations/user-store";
|
import { getAppVersion } from "../utils/app-version";
|
||||||
import { getAppVersion } from "./utils/app-version";
|
import { kubeConfigDefaultPath } from "../kube-helpers";
|
||||||
import { appEventBus } from "./event-bus";
|
import { appEventBus } from "../event-bus";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
import os from "os";
|
import { fileNameMigration } from "../../migrations/user-store";
|
||||||
import { fileNameMigration } from "../migrations/user-store";
|
import { ObservableToggleSet, toJS } from "../../renderer/utils";
|
||||||
import { ObservableToggleSet, toJS } from "../renderer/utils";
|
import { DESCRIPTORS, KubeconfigSyncValue, UserPreferencesModel } from "./preferences-helpers";
|
||||||
|
|
||||||
export interface UserStoreModel {
|
export interface UserStoreModel {
|
||||||
lastSeenAppVersion: string;
|
lastSeenAppVersion: string;
|
||||||
preferences: UserPreferencesModel;
|
preferences: UserPreferencesModel;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface KubeconfigSyncEntry extends KubeconfigSyncValue {
|
export class UserStore extends BaseStore<UserStoreModel> /* implements UserStoreFlatModel (when strict null is enabled) */ {
|
||||||
filePath: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface KubeconfigSyncValue { }
|
|
||||||
|
|
||||||
export interface UserPreferencesModel {
|
|
||||||
httpsProxy?: string;
|
|
||||||
shell?: string;
|
|
||||||
colorTheme?: string;
|
|
||||||
localeTimezone?: string;
|
|
||||||
allowUntrustedCAs?: boolean;
|
|
||||||
allowTelemetry?: boolean;
|
|
||||||
downloadMirror?: string | "default";
|
|
||||||
downloadKubectlBinaries?: boolean;
|
|
||||||
downloadBinariesPath?: string;
|
|
||||||
kubectlBinariesPath?: string;
|
|
||||||
openAtLogin?: boolean;
|
|
||||||
hiddenTableColumns?: [string, string[]][];
|
|
||||||
syncKubeconfigEntries?: KubeconfigSyncEntry[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export class UserStore extends BaseStore<UserStoreModel> {
|
|
||||||
static readonly defaultTheme: ThemeId = "lens-dark";
|
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super({
|
super({
|
||||||
configName: "lens-user-store",
|
configName: "lens-user-store",
|
||||||
@ -75,11 +50,18 @@ export class UserStore extends BaseStore<UserStoreModel> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@observable lastSeenAppVersion = "0.0.0";
|
@observable lastSeenAppVersion = "0.0.0";
|
||||||
@observable allowTelemetry = true;
|
|
||||||
@observable allowUntrustedCAs = false;
|
/**
|
||||||
@observable colorTheme = UserStore.defaultTheme;
|
* used in add-cluster page for providing context
|
||||||
@observable localeTimezone = moment.tz.guess(true) || "UTC";
|
*/
|
||||||
@observable downloadMirror = "default";
|
@observable kubeConfigPath = kubeConfigDefaultPath;
|
||||||
|
@observable seenContexts = observable.set<string>();
|
||||||
|
@observable newContexts = observable.set<string>();
|
||||||
|
@observable allowTelemetry: boolean;
|
||||||
|
@observable allowUntrustedCAs: boolean;
|
||||||
|
@observable colorTheme: string;
|
||||||
|
@observable localeTimezone: string;
|
||||||
|
@observable downloadMirror: string;
|
||||||
@observable httpsProxy?: string;
|
@observable httpsProxy?: string;
|
||||||
@observable shell?: string;
|
@observable shell?: string;
|
||||||
@observable downloadBinariesPath?: string;
|
@observable downloadBinariesPath?: string;
|
||||||
@ -88,8 +70,8 @@ export class UserStore extends BaseStore<UserStoreModel> {
|
|||||||
/**
|
/**
|
||||||
* Download kubectl binaries matching cluster version
|
* Download kubectl binaries matching cluster version
|
||||||
*/
|
*/
|
||||||
@observable downloadKubectlBinaries = true;
|
@observable downloadKubectlBinaries: boolean;
|
||||||
@observable openAtLogin = false;
|
@observable openAtLogin: boolean;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The column IDs under each configurable table ID that have been configured
|
* The column IDs under each configurable table ID that have been configured
|
||||||
@ -100,9 +82,7 @@ export class UserStore extends BaseStore<UserStoreModel> {
|
|||||||
/**
|
/**
|
||||||
* The set of file/folder paths to be synced
|
* The set of file/folder paths to be synced
|
||||||
*/
|
*/
|
||||||
syncKubeconfigEntries = observable.map<string, KubeconfigSyncValue>([
|
syncKubeconfigEntries = observable.map<string, KubeconfigSyncValue>();
|
||||||
[path.join(os.homedir(), ".kube"), {}]
|
|
||||||
]);
|
|
||||||
|
|
||||||
@computed get isNewVersion() {
|
@computed get isNewVersion() {
|
||||||
return semver.gt(getAppVersion(), this.lastSeenAppVersion);
|
return semver.gt(getAppVersion(), this.lastSeenAppVersion);
|
||||||
@ -160,7 +140,7 @@ export class UserStore extends BaseStore<UserStoreModel> {
|
|||||||
|
|
||||||
@action
|
@action
|
||||||
resetTheme() {
|
resetTheme() {
|
||||||
this.colorTheme = UserStore.defaultTheme;
|
this.colorTheme = DESCRIPTORS.colorTheme.fromStore(undefined);
|
||||||
}
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
@ -175,71 +155,45 @@ export class UserStore extends BaseStore<UserStoreModel> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
protected async fromStore(data: Partial<UserStoreModel> = {}) {
|
protected fromStore(data: Partial<UserStoreModel> = {}) {
|
||||||
const { lastSeenAppVersion, preferences } = data;
|
const { lastSeenAppVersion, preferences } = data;
|
||||||
|
|
||||||
if (lastSeenAppVersion) {
|
if (lastSeenAppVersion) {
|
||||||
this.lastSeenAppVersion = lastSeenAppVersion;
|
this.lastSeenAppVersion = lastSeenAppVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!preferences) {
|
this.httpsProxy = DESCRIPTORS.httpsProxy.fromStore(preferences?.httpsProxy);
|
||||||
return;
|
this.shell = DESCRIPTORS.shell.fromStore(preferences?.shell);
|
||||||
}
|
this.colorTheme = DESCRIPTORS.colorTheme.fromStore(preferences?.colorTheme);
|
||||||
|
this.localeTimezone = DESCRIPTORS.localeTimezone.fromStore(preferences?.localeTimezone);
|
||||||
this.httpsProxy = preferences.httpsProxy;
|
this.allowUntrustedCAs = DESCRIPTORS.allowUntrustedCAs.fromStore(preferences?.allowUntrustedCAs);
|
||||||
this.shell = preferences.shell;
|
this.allowTelemetry = DESCRIPTORS.allowTelemetry.fromStore(preferences?.allowTelemetry);
|
||||||
this.colorTheme = preferences.colorTheme;
|
this.downloadMirror = DESCRIPTORS.downloadMirror.fromStore(preferences?.downloadMirror);
|
||||||
this.localeTimezone = preferences.localeTimezone;
|
this.downloadKubectlBinaries = DESCRIPTORS.downloadKubectlBinaries.fromStore(preferences?.downloadKubectlBinaries);
|
||||||
this.allowUntrustedCAs = preferences.allowUntrustedCAs;
|
this.downloadBinariesPath = DESCRIPTORS.downloadBinariesPath.fromStore(preferences?.downloadBinariesPath);
|
||||||
this.allowTelemetry = preferences.allowTelemetry;
|
this.kubectlBinariesPath = DESCRIPTORS.kubectlBinariesPath.fromStore(preferences?.kubectlBinariesPath);
|
||||||
this.downloadMirror = preferences.downloadMirror;
|
this.openAtLogin = DESCRIPTORS.openAtLogin.fromStore(preferences?.openAtLogin);
|
||||||
this.downloadKubectlBinaries = preferences.downloadKubectlBinaries;
|
this.hiddenTableColumns.replace(DESCRIPTORS.hiddenTableColumns.fromStore(preferences?.hiddenTableColumns));
|
||||||
this.downloadBinariesPath = preferences.downloadBinariesPath;
|
this.syncKubeconfigEntries.replace(DESCRIPTORS.syncKubeconfigEntries.fromStore(preferences?.syncKubeconfigEntries));
|
||||||
this.kubectlBinariesPath = preferences.kubectlBinariesPath;
|
|
||||||
this.openAtLogin = preferences.openAtLogin;
|
|
||||||
|
|
||||||
if (preferences.hiddenTableColumns) {
|
|
||||||
this.hiddenTableColumns.replace(
|
|
||||||
preferences.hiddenTableColumns
|
|
||||||
.map(([tableId, columnIds]) => [tableId, new ObservableToggleSet(columnIds)])
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (preferences.syncKubeconfigEntries) {
|
|
||||||
this.syncKubeconfigEntries.replace(
|
|
||||||
preferences.syncKubeconfigEntries.map(({ filePath, ...rest }) => [filePath, rest])
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
toJSON(): UserStoreModel {
|
toJSON(): UserStoreModel {
|
||||||
const hiddenTableColumns: [string, string[]][] = [];
|
|
||||||
const syncKubeconfigEntries: KubeconfigSyncEntry[] = [];
|
|
||||||
|
|
||||||
for (const [key, values] of this.hiddenTableColumns.entries()) {
|
|
||||||
hiddenTableColumns.push([key, Array.from(values)]);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const [filePath, rest] of this.syncKubeconfigEntries) {
|
|
||||||
syncKubeconfigEntries.push({ filePath, ...rest });
|
|
||||||
}
|
|
||||||
|
|
||||||
const model: UserStoreModel = {
|
const model: UserStoreModel = {
|
||||||
lastSeenAppVersion: this.lastSeenAppVersion,
|
lastSeenAppVersion: this.lastSeenAppVersion,
|
||||||
preferences: {
|
preferences: {
|
||||||
httpsProxy: toJS(this.httpsProxy),
|
httpsProxy: DESCRIPTORS.httpsProxy.toStore(this.httpsProxy),
|
||||||
shell: toJS(this.shell),
|
shell: DESCRIPTORS.shell.toStore(this.shell),
|
||||||
colorTheme: toJS(this.colorTheme),
|
colorTheme: DESCRIPTORS.colorTheme.toStore(this.colorTheme),
|
||||||
localeTimezone: toJS(this.localeTimezone),
|
localeTimezone: DESCRIPTORS.localeTimezone.toStore(this.localeTimezone),
|
||||||
allowUntrustedCAs: toJS(this.allowUntrustedCAs),
|
allowUntrustedCAs: DESCRIPTORS.allowUntrustedCAs.toStore(this.allowUntrustedCAs),
|
||||||
allowTelemetry: toJS(this.allowTelemetry),
|
allowTelemetry: DESCRIPTORS.allowTelemetry.toStore(this.allowTelemetry),
|
||||||
downloadMirror: toJS(this.downloadMirror),
|
downloadMirror: DESCRIPTORS.downloadMirror.toStore(this.downloadMirror),
|
||||||
downloadKubectlBinaries: toJS(this.downloadKubectlBinaries),
|
downloadKubectlBinaries: DESCRIPTORS.downloadKubectlBinaries.toStore(this.downloadKubectlBinaries),
|
||||||
downloadBinariesPath: toJS(this.downloadBinariesPath),
|
downloadBinariesPath: DESCRIPTORS.downloadBinariesPath.toStore(this.downloadBinariesPath),
|
||||||
kubectlBinariesPath: toJS(this.kubectlBinariesPath),
|
kubectlBinariesPath: DESCRIPTORS.kubectlBinariesPath.toStore(this.kubectlBinariesPath),
|
||||||
openAtLogin: toJS(this.openAtLogin),
|
openAtLogin: DESCRIPTORS.openAtLogin.toStore(this.openAtLogin),
|
||||||
hiddenTableColumns,
|
hiddenTableColumns: DESCRIPTORS.hiddenTableColumns.toStore(this.hiddenTableColumns),
|
||||||
syncKubeconfigEntries,
|
syncKubeconfigEntries: DESCRIPTORS.syncKubeconfigEntries.toStore(this.syncKubeconfigEntries),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -46,12 +46,15 @@ export class Singleton {
|
|||||||
static createInstance<T, R extends any[]>(this: StaticThis<T, R>, ...args: R): T {
|
static createInstance<T, R extends any[]>(this: StaticThis<T, R>, ...args: R): T {
|
||||||
if (!Singleton.instances.has(this)) {
|
if (!Singleton.instances.has(this)) {
|
||||||
if (Singleton.creating.length > 0) {
|
if (Singleton.creating.length > 0) {
|
||||||
throw new TypeError("Cannot create a second singleton while creating a first");
|
throw new TypeError(`Cannot create a second singleton (${this.name}) while creating a first (${Singleton.creating})`);
|
||||||
}
|
}
|
||||||
|
|
||||||
Singleton.creating = this.name;
|
try {
|
||||||
Singleton.instances.set(this, new this(...args));
|
Singleton.creating = this.name;
|
||||||
Singleton.creating = "";
|
Singleton.instances.set(this, new this(...args));
|
||||||
|
} finally {
|
||||||
|
Singleton.creating = "";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Singleton.instances.get(this) as T;
|
return Singleton.instances.get(this) as T;
|
||||||
|
|||||||
@ -58,7 +58,8 @@ export class WeblinkStore extends BaseStore<WeblinkStoreModel> {
|
|||||||
this.load();
|
this.load();
|
||||||
}
|
}
|
||||||
|
|
||||||
@action protected async fromStore(data: Partial<WeblinkStoreModel> = {}) {
|
@action
|
||||||
|
protected fromStore(data: Partial<WeblinkStoreModel> = {}) {
|
||||||
this.weblinks = data.weblinks || [];
|
this.weblinks = data.weblinks || [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -46,6 +46,8 @@ export function joinMigrations(...declarations: MigrationDeclaration[]): Migrati
|
|||||||
iter.map(
|
iter.map(
|
||||||
migrations,
|
migrations,
|
||||||
([v, fns]) => [v, (store: Conf<any>) => {
|
([v, fns]) => [v, (store: Conf<any>) => {
|
||||||
|
migrationLog(`Running ${v} migration for ${store.path}`);
|
||||||
|
|
||||||
for (const fn of fns) {
|
for (const fn of fns) {
|
||||||
fn(store);
|
fn(store);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -75,6 +75,11 @@ export default {
|
|||||||
for (const workspaceId of cluster.workspaces ?? [cluster.workspace].filter(Boolean)) {
|
for (const workspaceId of cluster.workspaces ?? [cluster.workspace].filter(Boolean)) {
|
||||||
const workspaceHotbar = workspaceHotbars.get(workspaceId);
|
const workspaceHotbar = workspaceHotbars.get(workspaceId);
|
||||||
|
|
||||||
|
if (!workspaceHotbar) {
|
||||||
|
migrationLog(`Cluster ${uid} has unknown workspace ID, skipping`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
migrationLog(`Adding cluster ${uid} to ${workspaceHotbar.name}`);
|
migrationLog(`Adding cluster ${uid} to ${workspaceHotbar.name}`);
|
||||||
|
|
||||||
if (workspaceHotbar?.items.length < defaultHotbarCells) {
|
if (workspaceHotbar?.items.length < defaultHotbarCells) {
|
||||||
|
|||||||
@ -35,6 +35,7 @@ import { clusterViewURL } from "../../../common/routes";
|
|||||||
@observer
|
@observer
|
||||||
export class CommandDialog extends React.Component {
|
export class CommandDialog extends React.Component {
|
||||||
@observable menuIsOpen = true;
|
@observable menuIsOpen = true;
|
||||||
|
@observable searchValue: any = undefined;
|
||||||
|
|
||||||
constructor(props: {}) {
|
constructor(props: {}) {
|
||||||
super(props);
|
super(props);
|
||||||
@ -104,14 +105,24 @@ export class CommandDialog extends React.Component {
|
|||||||
return (
|
return (
|
||||||
<Select
|
<Select
|
||||||
menuPortalTarget={null}
|
menuPortalTarget={null}
|
||||||
onChange={(v) => this.onChange(v.value)}
|
onChange={v => this.onChange(v.value)}
|
||||||
components={{ DropdownIndicator: null, IndicatorSeparator: null }}
|
components={{
|
||||||
|
DropdownIndicator: null,
|
||||||
|
IndicatorSeparator: null,
|
||||||
|
}}
|
||||||
menuIsOpen={this.menuIsOpen}
|
menuIsOpen={this.menuIsOpen}
|
||||||
options={this.options}
|
options={this.options}
|
||||||
autoFocus={true}
|
autoFocus={true}
|
||||||
escapeClearsValue={false}
|
escapeClearsValue={false}
|
||||||
data-test-id="command-palette-search"
|
data-test-id="command-palette-search"
|
||||||
placeholder="Type a command or search…" />
|
placeholder="Type a command or search…"
|
||||||
|
onInputChange={(newValue, { action }) => {
|
||||||
|
if (action === "input-change") {
|
||||||
|
this.searchValue = newValue;
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
inputValue={this.searchValue}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -72,7 +72,7 @@
|
|||||||
bottom: 0;
|
bottom: 0;
|
||||||
width: 0;
|
width: 0;
|
||||||
height: $unit /2;
|
height: $unit /2;
|
||||||
transition: width 250ms;
|
transition: width 150ms;
|
||||||
background: currentColor;
|
background: currentColor;
|
||||||
color: $halfGray
|
color: $halfGray
|
||||||
}
|
}
|
||||||
|
|||||||
@ -46,6 +46,7 @@ export interface ThemeItems extends Theme {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class ThemeStore extends Singleton {
|
export class ThemeStore extends Singleton {
|
||||||
|
static readonly defaultTheme = "lens-dark";
|
||||||
protected styles: HTMLStyleElement;
|
protected styles: HTMLStyleElement;
|
||||||
|
|
||||||
// bundled themes from `themes/${themeId}.json`
|
// bundled themes from `themes/${themeId}.json`
|
||||||
|
|||||||
@ -5012,10 +5012,10 @@ domhandler@^2.3.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
domelementtype "1"
|
domelementtype "1"
|
||||||
|
|
||||||
dompurify@^2.0.11:
|
dompurify@^2.0.17:
|
||||||
version "2.0.11"
|
version "2.0.17"
|
||||||
resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-2.0.11.tgz#cd47935774230c5e478b183a572e726300b3891d"
|
resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-2.0.17.tgz#505ffa126a580603df4007e034bdc9b6b738668e"
|
||||||
integrity sha512-qVoGPjIW9IqxRij7klDQQ2j6nSe4UNWANBhZNLnsS7ScTtLb+3YdxkRY8brNTpkUiTtcXsCJO+jS0UCDfenLuA==
|
integrity sha512-nNwwJfW55r8akD8MSFz6k75bzyT2y6JEa1O3JrZFBf+Y5R9JXXU4OsRl0B9hKoPgHTw2b7ER5yJ5Md97MMUJPg==
|
||||||
|
|
||||||
domutils@1.5.1:
|
domutils@1.5.1:
|
||||||
version "1.5.1"
|
version "1.5.1"
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user