From 16a26724d94c05bb9062cf06d69e62213263abdd Mon Sep 17 00:00:00 2001 From: Mario Sarcher Date: Wed, 8 Dec 2021 08:12:29 +0100 Subject: [PATCH 01/17] Fixes a typo in kubeconfig sync "add"-button label (#4522) Signed-off-by: Mario Sarcher --- src/renderer/initializers/catalog-category-registry.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/renderer/initializers/catalog-category-registry.tsx b/src/renderer/initializers/catalog-category-registry.tsx index 0c1fd6b9b3..294c5883fa 100644 --- a/src/renderer/initializers/catalog-category-registry.tsx +++ b/src/renderer/initializers/catalog-category-registry.tsx @@ -61,11 +61,11 @@ export function initCatalogCategoryRegistryEntries() { ctx.menuItems.push( { icon: "create_new_folder", - title: "Sync kubeconfig folders(s)", + title: "Sync kubeconfig folder(s)", defaultAction: true, onClick: async () => { await PathPicker.pick({ - label: "Sync folders(s)", + label: "Sync folder(s)", buttonLabel: "Sync", properties: ["showHiddenFiles", "multiSelections", "openDirectory"], onPick: addSyncEntries, From 889a4d5080d874cad5daf342f75a36d01075cd2f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 8 Dec 2021 15:55:14 +0300 Subject: [PATCH 02/17] Bump @testing-library/jest-dom from 5.15.0 to 5.16.1 (#4516) Bumps [@testing-library/jest-dom](https://github.com/testing-library/jest-dom) from 5.15.0 to 5.16.1. - [Release notes](https://github.com/testing-library/jest-dom/releases) - [Changelog](https://github.com/testing-library/jest-dom/blob/main/CHANGELOG.md) - [Commits](https://github.com/testing-library/jest-dom/compare/v5.15.0...v5.16.1) --- updated-dependencies: - dependency-name: "@testing-library/jest-dom" dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 15 ++++++++++----- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index a5db133e96..6583d849b1 100644 --- a/package.json +++ b/package.json @@ -271,7 +271,7 @@ "@material-ui/lab": "^4.0.0-alpha.60", "@pmmmwh/react-refresh-webpack-plugin": "^0.4.3", "@sentry/types": "^6.14.1", - "@testing-library/jest-dom": "^5.15.0", + "@testing-library/jest-dom": "^5.16.1", "@testing-library/react": "^11.2.7", "@testing-library/user-event": "^13.5.0", "@types/byline": "^4.2.33", diff --git a/yarn.lock b/yarn.lock index fe53a30161..410f77fe9d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1210,14 +1210,14 @@ lz-string "^1.4.4" pretty-format "^26.6.2" -"@testing-library/jest-dom@^5.15.0": - version "5.15.0" - resolved "https://registry.yarnpkg.com/@testing-library/jest-dom/-/jest-dom-5.15.0.tgz#4f5295dbc476a14aec3b07176434b3d51aae5da7" - integrity sha512-lOMuQidnL1tWHLEWIhL6UvSZC1Qt3OkNe1khvi2h6xFiqpe5O8arYs46OU0qyUGq0cSTbroQyMktYNXu3a7sAA== +"@testing-library/jest-dom@^5.16.1": + version "5.16.1" + resolved "https://registry.yarnpkg.com/@testing-library/jest-dom/-/jest-dom-5.16.1.tgz#3db7df5ae97596264a7da9696fe14695ba02e51f" + integrity sha512-ajUJdfDIuTCadB79ukO+0l8O+QwN0LiSxDaYUTI4LndbbUsGi6rWU1SCexXzBA2NSjlVB9/vbkasQIL3tmPBjw== dependencies: "@babel/runtime" "^7.9.2" "@types/testing-library__jest-dom" "^5.9.1" - aria-query "^4.2.2" + aria-query "^5.0.0" chalk "^3.0.0" css "^3.0.0" css.escape "^1.5.1" @@ -2653,6 +2653,11 @@ aria-query@^4.2.2: "@babel/runtime" "^7.10.2" "@babel/runtime-corejs3" "^7.10.2" +aria-query@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-5.0.0.tgz#210c21aaf469613ee8c9a62c7f86525e058db52c" + integrity sha512-V+SM7AbUwJ+EBnB8+DXs0hPZHO0W6pqBcc0dW90OwtVG02PswOu/teuARoLQjdDOH+t9pJgGnW5/Qmouf3gPJg== + arr-diff@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" From 25f4a1f3ba6da9bd845cdb05516e9159ced89b98 Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Thu, 9 Dec 2021 06:35:49 -0500 Subject: [PATCH 03/17] Upgrade mock-fs to v5 (#4537) Signed-off-by: Sebastian Malton --- package.json | 6 +++--- yarn.lock | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/package.json b/package.json index 6583d849b1..f062889b35 100644 --- a/package.json +++ b/package.json @@ -196,10 +196,10 @@ "@hapi/call": "^8.0.1", "@hapi/subtext": "^7.0.3", "@kubernetes/client-node": "^0.16.1", - "@sentry/electron": "^2.5.4", - "@sentry/integrations": "^6.15.0", "@ogre-tools/injectable": "^1.3.0", "@ogre-tools/injectable-react": "^1.3.1", + "@sentry/electron": "^2.5.4", + "@sentry/integrations": "^6.15.0", "abort-controller": "^3.0.0", "auto-bind": "^4.0.0", "autobind-decorator": "^2.4.0", @@ -230,7 +230,7 @@ "mobx": "^6.3.7", "mobx-observable-history": "^2.0.3", "mobx-react": "^7.2.1", - "mock-fs": "^4.14.0", + "mock-fs": "^5.1.2", "moment": "^2.29.1", "moment-timezone": "^0.5.34", "monaco-editor": "^0.29.1", diff --git a/yarn.lock b/yarn.lock index 410f77fe9d..bab3877a8a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9578,10 +9578,10 @@ mobx@^6.3.7: resolved "https://registry.yarnpkg.com/mobx/-/mobx-6.3.7.tgz#9ed85561e86da45141134c8fa20cf5f9c7246c3d" integrity sha512-X7yU7eOEyxIBk4gjIi2UIilwdw48gXh0kcZ5ex3Rc+COJsJmJ4SNpf42uYea3aUqb1hedTv5xzJrq5Q55p0P5g== -mock-fs@^4.14.0: - version "4.14.0" - resolved "https://registry.yarnpkg.com/mock-fs/-/mock-fs-4.14.0.tgz#ce5124d2c601421255985e6e94da80a7357b1b18" - integrity sha512-qYvlv/exQ4+svI3UOvPUpLDF0OMX5euvUH0Ny4N5QyRyhNdgAgUrVH3iUINSzEPLvx0kbo/Bp28GJKIqvE7URw== +mock-fs@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/mock-fs/-/mock-fs-5.1.2.tgz#6fa486e06d00f8793a8d2228de980eff93ce6db7" + integrity sha512-YkjQkdLulFrz0vD4BfNQdQRVmgycXTV7ykuHMlyv+C8WCHazpkiQRDthwa02kSyo8wKnY9wRptHfQLgmf0eR+A== modern-normalize@^1.1.0: version "1.1.0" From 3dd6b432c96d579bced8a1998a3e40948b4816f3 Mon Sep 17 00:00:00 2001 From: Alex Andreev Date: Thu, 9 Dec 2021 15:11:34 +0300 Subject: [PATCH 04/17] Export KubeJsonApi to extensions (#4496) * Export KubeJsonApi to extensions Signed-off-by: Alex Andreev * Make forCluster() to consume clusterId as arg Signed-off-by: Alex Andreev * Fix lint Signed-off-by: Alex Andreev --- src/common/k8s-api/kube-json-api.ts | 5 ++--- src/extensions/renderer-api/k8s-api.ts | 1 + src/main/shell-session/node-shell-session.ts | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/common/k8s-api/kube-json-api.ts b/src/common/k8s-api/kube-json-api.ts index 3776634a47..59c8a7e980 100644 --- a/src/common/k8s-api/kube-json-api.ts +++ b/src/common/k8s-api/kube-json-api.ts @@ -21,7 +21,6 @@ import { JsonApi, JsonApiData, JsonApiError } from "./json-api"; import type { Response } from "node-fetch"; -import type { Cluster } from "../../main/cluster"; import { LensProxy } from "../../main/lens-proxy"; import { apiKubePrefix, isDebugging } from "../vars"; @@ -73,7 +72,7 @@ export interface KubeJsonApiError extends JsonApiError { } export class KubeJsonApi extends JsonApi { - static forCluster(cluster: Cluster): KubeJsonApi { + static forCluster(clusterId: string): KubeJsonApi { const port = LensProxy.getInstance().port; return new this({ @@ -82,7 +81,7 @@ export class KubeJsonApi extends JsonApi { debug: isDebugging, }, { headers: { - "Host": `${cluster.id}.localhost:${port}`, + "Host": `${clusterId}.localhost:${port}`, }, }); } diff --git a/src/extensions/renderer-api/k8s-api.ts b/src/extensions/renderer-api/k8s-api.ts index 4a084e6373..ecae80fb5a 100644 --- a/src/extensions/renderer-api/k8s-api.ts +++ b/src/extensions/renderer-api/k8s-api.ts @@ -55,6 +55,7 @@ export { ClusterRole, clusterRoleApi } from "../../common/k8s-api/endpoints"; export { ClusterRoleBinding, clusterRoleBindingApi } from "../../common/k8s-api/endpoints"; export { CustomResourceDefinition, crdApi } from "../../common/k8s-api/endpoints"; export { KubeObjectStatusLevel } from "./kube-object-status"; +export { KubeJsonApi } from "../../common/k8s-api/kube-json-api"; // types export type { ILocalKubeApiConfig, IRemoteKubeApiConfig, IKubeApiCluster } from "../../common/k8s-api/kube-api"; diff --git a/src/main/shell-session/node-shell-session.ts b/src/main/shell-session/node-shell-session.ts index 1f1a6cab03..b865fc411d 100644 --- a/src/main/shell-session/node-shell-session.ts +++ b/src/main/shell-session/node-shell-session.ts @@ -64,7 +64,7 @@ export class NodeShellSession extends ShellSession { const args = ["exec", "-i", "-t", "-n", "kube-system", this.podName, "--"]; const nodeApi = new NodesApi({ objectConstructor: Node, - request: KubeJsonApi.forCluster(this.cluster), + request: KubeJsonApi.forCluster(this.cluster.id), }); const node = await nodeApi.get({ name: this.nodeName }); const nodeOs = node.getOperatingSystem(); From 541c00cf2420a7d2dd67fa799b03e3e578cd6c04 Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Thu, 9 Dec 2021 08:40:06 -0500 Subject: [PATCH 05/17] Add tooltip for cluster name in sidebar (#4534) --- .../layout/__tests__/sidebar-cluster.test.tsx | 15 ++++-- .../layout/sidebar-cluster.module.css | 49 ++++++++++++++++++- .../components/layout/sidebar-cluster.tsx | 23 ++++++++- 3 files changed, 80 insertions(+), 7 deletions(-) diff --git a/src/renderer/components/layout/__tests__/sidebar-cluster.test.tsx b/src/renderer/components/layout/__tests__/sidebar-cluster.test.tsx index 195368408d..c256c03e76 100644 --- a/src/renderer/components/layout/__tests__/sidebar-cluster.test.tsx +++ b/src/renderer/components/layout/__tests__/sidebar-cluster.test.tsx @@ -57,18 +57,25 @@ describe("", () => { }); it("renders cluster avatar and name", () => { - const { getByText } = render(); + const { getByText, getAllByText } = render(); expect(getByText("tc")).toBeInTheDocument(); - expect(getByText("test-cluster")).toBeInTheDocument(); + + const v = getAllByText("test-cluster"); + + expect(v.length).toBeGreaterThan(0); + + for (const e of v) { + expect(e).toBeInTheDocument(); + } }); - it("renders cluster menu", async () => { + it("renders cluster menu", () => { const { getByTestId, getByText } = render(); const link = getByTestId("sidebar-cluster-dropdown"); fireEvent.click(link); - expect(await getByText("Add to Hotbar")).toBeInTheDocument(); + expect(getByText("Add to Hotbar")).toBeInTheDocument(); }); }); diff --git a/src/renderer/components/layout/sidebar-cluster.module.css b/src/renderer/components/layout/sidebar-cluster.module.css index 635443500d..2b57854776 100644 --- a/src/renderer/components/layout/sidebar-cluster.module.css +++ b/src/renderer/components/layout/sidebar-cluster.module.css @@ -61,4 +61,51 @@ .avatar { font-weight: 500; margin-right: 1.25rem; -} \ No newline at end of file +} + +.loadingAvatar { + position: relative; + pointer-events: none; + &:after { + content: ""; + position: absolute; + left: 0; + top: 0; + width: 0; + height: 100%; + background: transparentize(white, .85); + animation: waiting 1.5s infinite linear; + } +} + +.loadingClusterName { + position: relative; + pointer-events: none; + width: 80%; + height: 16px; + &:after { + content: ""; + position: absolute; + left: 0; + top: 0; + width: 0; + height: 100%; + background: transparentize(white, .85); + animation: waiting 1.5s infinite linear; + } +} + +@keyframes waiting { + 0% { + left: 0; + width: 0; + } + 50% { + left: 25%; + width: 75%; + } + 75% { + left: 100%; + width: 0; + } +} diff --git a/src/renderer/components/layout/sidebar-cluster.tsx b/src/renderer/components/layout/sidebar-cluster.tsx index baa77f168e..a4479f1a24 100644 --- a/src/renderer/components/layout/sidebar-cluster.tsx +++ b/src/renderer/components/layout/sidebar-cluster.tsx @@ -31,6 +31,7 @@ import { Icon } from "../icon"; import { navigate } from "../../navigation"; import { Menu, MenuItem } from "../menu"; import { ConfirmDialog } from "../confirm-dialog"; +import { Tooltip } from "../tooltip"; const contextMenu: CatalogEntityContextMenuContext = observable({ menuItems: [], @@ -60,11 +61,25 @@ function onMenuItemClick(menuItem: CatalogEntityContextMenu) { } } +function renderLoadingSidebarCluster() { + return ( +
+ +
+
+ ); +} + export function SidebarCluster({ clusterEntity }: { clusterEntity: CatalogEntity }) { const [opened, setOpened] = useState(false); if (!clusterEntity) { - return null; + return renderLoadingSidebarCluster(); } const onMenuOpen = () => { @@ -95,6 +110,7 @@ export function SidebarCluster({ clusterEntity }: { clusterEntity: CatalogEntity const { metadata, spec } = clusterEntity; const id = `cluster-${metadata.uid}`; + const tooltipId = `tooltip-${id}`; return (
-
+
{metadata.name}
+ + {metadata.name} + Date: Thu, 9 Dec 2021 15:52:27 -0500 Subject: [PATCH 06/17] Fix freeze after closing terminal on windows (#4536) --- src/main/shell-session/shell-session.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/shell-session/shell-session.ts b/src/main/shell-session/shell-session.ts index b171986d56..677a637cc0 100644 --- a/src/main/shell-session/shell-session.ts +++ b/src/main/shell-session/shell-session.ts @@ -158,6 +158,8 @@ export abstract class ShellSession { cwd, env, name: "xterm-256color", + // TODO: Something else is broken here so we need to force the use of winPty on windows + useConpty: false, })); } From 6284bd1eb4b26e733a1f1a71b4d3e34178c8edc7 Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Thu, 9 Dec 2021 16:03:18 -0500 Subject: [PATCH 07/17] Fix crash in ClusterStatus (#4533) --- src/renderer/components/cluster-manager/cluster-status.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/renderer/components/cluster-manager/cluster-status.tsx b/src/renderer/components/cluster-manager/cluster-status.tsx index e926a75d84..79df15c412 100644 --- a/src/renderer/components/cluster-manager/cluster-status.tsx +++ b/src/renderer/components/cluster-manager/cluster-status.tsx @@ -153,7 +153,7 @@ export class ClusterStatus extends React.Component { return (
-

{this.entity.getName()}

+

{this.entity?.getName() ?? this.cluster.name}

{this.renderStatusIcon()} {this.renderAuthenticationOutput()} {this.renderReconnectionHelp()} From 747ef8daee5fc804350849834cfed5ab66f5e0b0 Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Fri, 10 Dec 2021 09:07:07 -0500 Subject: [PATCH 08/17] Special case goBack to navigate to catalog if no where to go back to (#4544) --- src/renderer/components/layout/setting-layout.tsx | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/renderer/components/layout/setting-layout.tsx b/src/renderer/components/layout/setting-layout.tsx index 9f458658e8..282eed0dcb 100644 --- a/src/renderer/components/layout/setting-layout.tsx +++ b/src/renderer/components/layout/setting-layout.tsx @@ -26,6 +26,7 @@ import { observer } from "mobx-react"; import { cssNames, IClassName } from "../../utils"; import { navigation } from "../../navigation"; import { Icon } from "../icon"; +import { catalogURL } from "../../../common/routes"; export interface SettingLayoutProps extends React.DOMAttributes { className?: IClassName; @@ -39,7 +40,13 @@ export interface SettingLayoutProps extends React.DOMAttributes { const defaultProps: Partial = { provideBackButtonNavigation: true, contentGaps: true, - back: () => navigation.goBack(), + back: () => { + if (navigation.length <= 1) { + navigation.push(catalogURL()); + } else { + navigation.goBack(); + } + }, }; /** From 181197fad8a353bed36aa7e9f181525c2f167f53 Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Fri, 10 Dec 2021 09:07:18 -0500 Subject: [PATCH 09/17] Remove unnecesarry TerminalApi.destroy and TerminalApi.reconnect overloads (#4538) --- src/renderer/api/terminal-api.ts | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/renderer/api/terminal-api.ts b/src/renderer/api/terminal-api.ts index 8cca03538b..5f1acc9516 100644 --- a/src/renderer/api/terminal-api.ts +++ b/src/renderer/api/terminal-api.ts @@ -144,18 +144,6 @@ export class TerminalApi extends WebSocketApi { this.socket.binaryType = "arraybuffer"; } - destroy() { - if (!this.socket) return; - const controlCode = String.fromCharCode(4); // ctrl+d - - this.sendMessage({ type: TerminalChannels.STDIN, data: controlCode }); - setTimeout(() => super.destroy(), 2000); - } - - reconnect() { - super.reconnect(); - } - sendMessage(message: TerminalMessage) { return this.send(serialize(message)); } From 41e7664fe29b76d84ac8cb07e75c765cb4d3b5a9 Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Fri, 10 Dec 2021 09:07:57 -0500 Subject: [PATCH 10/17] Add some gh and jq help to the release guide (#4549) --- RELEASE_GUIDE.md | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/RELEASE_GUIDE.md b/RELEASE_GUIDE.md index 896d0139f3..50f8b759e2 100644 --- a/RELEASE_GUIDE.md +++ b/RELEASE_GUIDE.md @@ -6,6 +6,13 @@ Lens releases are built by CICD automatically on git tags. The typical release p 1. If doing a patch release checkout the `release/vMAJOR.MINOR` branch for the appropriate `MAJOR`/`MINOR` version and manually `cherry-pick` the PRs required for the patch that were commited to master. If there are any conflicts they must be resolved manually. If necessary, get assistance from the PR authors. +This can be helped (if you have the `gh` CLI installed) by running: +``` +gh api -XGET "/repos/lensapp/lens/pulls?state=closed&per_page=100" | jq -r '[.[] | select(.milestone.title == "") | select((.merged_at | type) == "string")] | sort_by(.merged_at) | map(.merge_commit_sha) | join(" ")' +``` + +But you will probably need to verify that all the PRs have correct milestones. + 1. From a clean and up to date `master` (or `release/vMAJOR.MINOR` if doing a patch release) run `npm version --git-tag-version false` where `` is one of the following: - `major` - `minor` @@ -14,14 +21,27 @@ Lens releases are built by CICD automatically on git tags. The typical release p - `preminor [--preid=]` - `prepatch [--preid=]` - `prerelease [--preid=]` - + where `` is generally one of: - `alpha` - `beta` - `rc` - + This assumes origin is set to https://github.com/lensapp/lens.git. If not then set GIT_REMOTE to the remote that is set to https://github.com/lensapp/lens.git. For example run `GIT_REMOTE=upstream npm version ...` 1. Open the PR (git should have printed a link to GitHub in the console) with the contents of all the accepted PRs since the last release. The PR description needs to be filled with the draft release description. From https://github.com/lensapp/lens click on Releases, the draft release should be first in the list, click `Edit` and copy/paste the markdown to the PR description. Add the `skip-changelog` label and click `Create Pull Request`. If this is a patch release be sure to set the PR base branch to `release/vMAJOR.MINOR` instead of `master`. + + It might also help, if the release drafter isn't updating correctly. To grab the data for the PR description using `gh` and `jq`. You can run the following command to get all the titles: + + ``` + gh api -XGET "/repos/lensapp/lens/pulls?state=closed&per_page=100" | jq -r '[.[] | select(.milestone.title == "") | select((.merged_at | type) == "string")] | sort_by(.merged_at) | map([.title, " (**#", .number, "**) ", .user.html_url] | join ("")) | join("\n")' | pbcopy + ``` + + And if you want to specify just bug fixes then you can add the following to the end of the `[ ... ]` section in the above command (before the `sort_by`) to just have bug fixes. Switch to `== "enhancement"` for enhancements and `all()` with `. != "bug && . != "enhancement"` for maintanence sections. + + ``` + | select(any(.labels | map(.name)[]; . == "bug")) + ``` + 1. After the PR is accepted and passes CI (and before merging), go to the same branch and run `make tag-release` (set GIT_REMOTE if necessary). This additionally triggers the azure jobs to build the binaries and put them on S3. 1. If the CI fails at this stage the problem needs to be fixed. Sometimes an azure job fails due to outside service issues (e.g. Apple signing occasionally fails), in which case the specific azure job can be rerun from https://dev.azure.com/lensapp/lensapp/_build. Otherwise changes to the codebase may need to be done and committed to the release branch and pushed to https://github.com/lensapp/lens. CI will run again. As well the release tag needs to be manually set to this new commit. You can do something like: - `git push origin :refs/tags/vX.Y.Z-beta.N` (removes the tag from https://github.com/lensapp/lens) @@ -43,4 +63,4 @@ Other tasks - generate a changelog from the prerelease descriptions (for major/minor releases) - announce the release on lens and lens-hq slack channels (release is announced automatically on the community slack lens channel through the above publishing process) - announce on lens-hq that master is open for PR merges for the next release (for major/minor releases) - - update issues on github (bump those that did not make it into the release to a subsequent release) (for major/minor/patch releases) \ No newline at end of file + - update issues on github (bump those that did not make it into the release to a subsequent release) (for major/minor/patch releases) From 1d1a85f9ea96fc6ea28192cbede71f40e492928d Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Mon, 13 Dec 2021 10:45:24 -0500 Subject: [PATCH 11/17] Use electron.clipboard for all clipboard uses (#4535) --- .../components/clipboard/clipboard.scss | 24 ----- .../components/clipboard/clipboard.tsx | 88 ------------------- src/renderer/components/clipboard/index.ts | 22 ----- .../components/dialog/logs-dialog.tsx | 7 +- .../kubeconfig-dialog/kubeconfig-dialog.tsx | 14 +-- src/renderer/utils/copyToClipboard.ts | 52 ----------- src/renderer/utils/index.ts | 1 - 7 files changed, 7 insertions(+), 201 deletions(-) delete mode 100644 src/renderer/components/clipboard/clipboard.scss delete mode 100644 src/renderer/components/clipboard/clipboard.tsx delete mode 100644 src/renderer/components/clipboard/index.ts delete mode 100644 src/renderer/utils/copyToClipboard.ts diff --git a/src/renderer/components/clipboard/clipboard.scss b/src/renderer/components/clipboard/clipboard.scss deleted file mode 100644 index b90314cc61..0000000000 --- a/src/renderer/components/clipboard/clipboard.scss +++ /dev/null @@ -1,24 +0,0 @@ -/** - * 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. - */ - -.Clipboard { - cursor: pointer; -} \ No newline at end of file diff --git a/src/renderer/components/clipboard/clipboard.tsx b/src/renderer/components/clipboard/clipboard.tsx deleted file mode 100644 index 6f5a7063e1..0000000000 --- a/src/renderer/components/clipboard/clipboard.tsx +++ /dev/null @@ -1,88 +0,0 @@ -/** - * 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 "./clipboard.scss"; -import React from "react"; -import { findDOMNode } from "react-dom"; -import { boundMethod } from "../../../common/utils"; -import { Notifications } from "../notifications"; -import { copyToClipboard } from "../../utils/copyToClipboard"; -import logger from "../../../main/logger"; -import { cssNames } from "../../utils"; - -export interface CopyToClipboardProps { - resetSelection?: boolean; - showNotification?: boolean; - cssSelectorLimit?: string; // allows to copy partial content with css-selector in children-element context - getNotificationMessage?(copiedText: string): React.ReactNode; -} - -export const defaultProps: Partial = { - getNotificationMessage(copiedText: string) { - return

Copied to clipboard: {copiedText}

; - }, -}; - -export class Clipboard extends React.Component { - static displayName = "Clipboard"; - static defaultProps = defaultProps as object; - - get rootElem(): HTMLElement { - // eslint-disable-next-line react/no-find-dom-node - return findDOMNode(this) as HTMLElement; - } - - get rootReactElem(): React.ReactElement> { - return React.Children.only(this.props.children) as React.ReactElement; - } - - @boundMethod - onClick(evt: React.MouseEvent) { - if (this.rootReactElem.props.onClick) { - this.rootReactElem.props.onClick(evt); // pass event to children-root-element if any - } - const { showNotification, resetSelection, getNotificationMessage, cssSelectorLimit } = this.props; - const contentElem = this.rootElem.querySelector(cssSelectorLimit) || this.rootElem; - - if (contentElem) { - const { copiedText, copied } = copyToClipboard(contentElem, { resetSelection }); - - if (copied && showNotification) { - Notifications.ok(getNotificationMessage(copiedText)); - } - } - } - - render() { - try { - const rootElem = this.rootReactElem; - - return React.cloneElement(rootElem, { - className: cssNames(Clipboard.displayName, rootElem.props.className), - onClick: this.onClick, - }); - } catch (err) { - logger.error(`Invalid usage components/CopyToClick usage. Children must contain root html element.`, { err: String(err) }); - - return this.rootReactElem; - } - } -} diff --git a/src/renderer/components/clipboard/index.ts b/src/renderer/components/clipboard/index.ts deleted file mode 100644 index 9f6ae846bc..0000000000 --- a/src/renderer/components/clipboard/index.ts +++ /dev/null @@ -1,22 +0,0 @@ -/** - * 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 "./clipboard"; diff --git a/src/renderer/components/dialog/logs-dialog.tsx b/src/renderer/components/dialog/logs-dialog.tsx index 83f685ba5b..4299129466 100644 --- a/src/renderer/components/dialog/logs-dialog.tsx +++ b/src/renderer/components/dialog/logs-dialog.tsx @@ -24,10 +24,10 @@ import "./logs-dialog.scss"; import React from "react"; import { Dialog, DialogProps } from "../dialog"; import { Wizard, WizardStep } from "../wizard"; -import { copyToClipboard } from "../../utils"; import { Notifications } from "../notifications"; import { Button } from "../button"; import { Icon } from "../icon"; +import { clipboard } from "electron"; // todo: make as external BrowserWindow (?) @@ -40,9 +40,8 @@ export class LogsDialog extends React.Component { public logsElem: HTMLElement; copyToClipboard = () => { - if (copyToClipboard(this.logsElem)) { - Notifications.ok(`Logs copied to clipboard.`); - } + clipboard.writeText(this.props.logs); + Notifications.ok(`Logs copied to clipboard.`); }; render() { diff --git a/src/renderer/components/kubeconfig-dialog/kubeconfig-dialog.tsx b/src/renderer/components/kubeconfig-dialog/kubeconfig-dialog.tsx index 8c9f6a44d1..02a240043a 100644 --- a/src/renderer/components/kubeconfig-dialog/kubeconfig-dialog.tsx +++ b/src/renderer/components/kubeconfig-dialog/kubeconfig-dialog.tsx @@ -25,7 +25,7 @@ import { makeObservable, observable } from "mobx"; import { observer } from "mobx-react"; import yaml from "js-yaml"; import type { ServiceAccount } from "../../../common/k8s-api/endpoints"; -import { copyToClipboard, saveFileDialog } from "../../utils"; +import { saveFileDialog } from "../../utils"; import { Button } from "../button"; import { Dialog, DialogProps } from "../dialog"; import { Icon } from "../icon"; @@ -33,6 +33,7 @@ import { Notifications } from "../notifications"; import { Wizard, WizardStep } from "../wizard"; import { apiBase } from "../../api"; import { MonacoEditor } from "../monaco-editor"; +import { clipboard } from "electron"; interface IKubeconfigDialogData { title?: React.ReactNode; @@ -49,7 +50,6 @@ const dialogState = observable.object({ @observer export class KubeConfigDialog extends React.Component { - @observable.ref configTextArea: HTMLTextAreaElement; // required for coping config text @observable config = ""; // parsed kubeconfig in yaml format constructor(props: Props) { @@ -89,9 +89,8 @@ export class KubeConfigDialog extends React.Component { } copyToClipboard = () => { - if (this.config && copyToClipboard(this.configTextArea)) { - Notifications.ok("Config copied to clipboard"); - } + clipboard.writeText(this.config); + Notifications.ok("Config copied to clipboard"); }; download = () => { @@ -131,11 +130,6 @@ export class KubeConfigDialog extends React.Component { className={styles.editor} value={yamlConfig} /> -