Skip to content

Commit c45ef94

Browse files
committed
Refactor and fix clipboard
1 parent 2eb3dda commit c45ef94

File tree

6 files changed

+51
-49
lines changed

6 files changed

+51
-49
lines changed

public/locales/en.json

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -82,8 +82,6 @@
8282
},
8383
"CopyKubeconfigButton": {
8484
"kubeconfigButton": "Kubeconfig",
85-
"copiedMessage": "Copied to Clipboard",
86-
"failedMessage": "Failed to copy, Error:",
8785
"menuDownload": "Download",
8886
"menuCopy": "Copy to clipboard"
8987
},
@@ -205,10 +203,6 @@
205203
"treeExecution": "Execution",
206204
"noExecutionFound": "No Executions found"
207205
},
208-
"CopyButton": {
209-
"copiedMessage": "Copied To Clipboard",
210-
"failedMessage": "Failed to copy"
211-
},
212206
"DeleteWorkspaceDialog": {
213207
"title": "Delete a Workspace",
214208
"introSection1": "The below instructions will help you delete the workspace \"{{workspaceName}}\" from project namespace \"{{projectNamespace}}\" using kubectl.",
@@ -341,7 +335,9 @@
341335
"unhealthy": "Unhealthy",
342336
"progress": "Managed",
343337
"remaining": "Remaining",
344-
"active": "Active"
338+
"active": "Active",
339+
"copyToClipboardSuccessToast": "Copied to clipboard",
340+
"copyToClipboardFailedToast": "Failed to copy to clipboard"
345341
},
346342
"errors": {
347343
"installError": "Install error",
@@ -360,7 +356,6 @@
360356
"cancel": "Cancel"
361357
},
362358
"yaml": {
363-
"copiedToClipboard": "YAML copied to clipboard!",
364359
"YAML": "File"
365360
},
366361
"createMCP": {

src/components/ControlPlanes/CopyKubeconfigButton.tsx

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Button, Menu, MenuItem } from '@ui5/webcomponents-react';
2-
import { useToast } from '../../context/ToastContext.tsx';
2+
import { useCopyToClipboard } from '../../hooks/useCopyToClipboard.ts';
33
import { useRef, useState } from 'react';
44
import '@ui5/webcomponents-icons/dist/copy';
55
import '@ui5/webcomponents-icons/dist/accept';
@@ -9,7 +9,7 @@ import { useTranslation } from 'react-i18next';
99
export default function CopyKubeconfigButton() {
1010
const popoverRef = useRef(null);
1111
const [open, setOpen] = useState(false);
12-
const { show } = useToast();
12+
const { copyToClipboard } = useCopyToClipboard();
1313
const { t } = useTranslation();
1414

1515
// eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -37,14 +37,7 @@ export default function CopyKubeconfigButton() {
3737
return;
3838
}
3939
if (event.detail.item.dataset.action === 'copy') {
40-
try {
41-
navigator.clipboard.writeText(mcp.kubeconfig ?? '');
42-
show(t('CopyKubeconfigButton.copiedMessage'));
43-
} catch (error) {
44-
//TODO: handle error, show error to user
45-
show(`${t('CopyKubeconfigButton.failedMessage')} ${error}`);
46-
console.error(error);
47-
}
40+
void copyToClipboard(mcp.kubeconfig ?? '');
4841
}
4942

5043
setOpen(false);

src/components/Dialogs/KubectlCommandInfo/KubectlTerminal.tsx

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { FlexBox, Button } from '@ui5/webcomponents-react';
2-
import { useToast } from '../../../context/ToastContext';
2+
import { useCopyToClipboard } from '../../../hooks/useCopyToClipboard.ts';
33
import '@ui5/webcomponents-icons/dist/copy';
44
import { ThemingParameters } from '@ui5/webcomponents-react-base';
55

@@ -8,18 +8,7 @@ interface KubeCtlTerminalProps {
88
}
99

1010
export const KubectlTerminal = ({ command }: KubeCtlTerminalProps) => {
11-
const { show } = useToast();
12-
13-
const handleCopy = () => {
14-
navigator.clipboard.writeText(command).then(
15-
() => {
16-
show('Command copied to clipboard');
17-
},
18-
(err) => {
19-
console.error('Could not copy text: ', err);
20-
},
21-
);
22-
};
11+
const { copyToClipboard } = useCopyToClipboard();
2312

2413
const FormattedCommand = () => {
2514
if (command.startsWith("echo '") && command.includes('apiVersion:')) {
@@ -91,7 +80,7 @@ export const KubectlTerminal = ({ command }: KubeCtlTerminalProps) => {
9180
}}
9281
/>
9382
</FlexBox>
94-
<Button icon="copy" design="Transparent" tooltip="Copy to clipboard" onClick={handleCopy} />
83+
<Button icon="copy" design="Transparent" tooltip="Copy to clipboard" onClick={() => copyToClipboard(command)} />
9584
</FlexBox>
9685

9786
<div style={{ padding: '12px 16px', overflowX: 'auto' }}>

src/components/Shared/CopyButton.tsx

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Button, ButtonPropTypes } from '@ui5/webcomponents-react';
2-
import { useToast } from '../../context/ToastContext.tsx';
2+
import { useCopyToClipboard } from '../../hooks/useCopyToClipboard.ts';
33
import { useId, CSSProperties } from 'react';
44
import { useCopyButton } from '../../context/CopyButtonContext.tsx';
55
import { ThemingParameters } from '@ui5/webcomponents-react-base';
@@ -11,20 +11,15 @@ interface CopyButtonProps extends ButtonPropTypes {
1111
}
1212

1313
export const CopyButton = ({ text, style = {}, ...buttonProps }: CopyButtonProps) => {
14-
const { show } = useToast();
14+
const { copyToClipboard } = useCopyToClipboard();
1515
const { activeCopyId, setActiveCopyId } = useCopyButton();
1616
const uniqueId = useId();
1717
const isCopied = activeCopyId === uniqueId;
1818
const { t } = useTranslation();
1919

2020
const handleCopy = async () => {
21-
try {
22-
await navigator.clipboard.writeText(text);
23-
setActiveCopyId(uniqueId);
24-
} catch (err) {
25-
console.error(`Failed to copy text: ${text}. Error: ${err}`);
26-
show(`${t('CopyButton.copiedMessage')} ${err}`);
27-
}
21+
await copyToClipboard(text, false);
22+
setActiveCopyId(uniqueId);
2823
};
2924

3025
const defaultStyle: CSSProperties = {
@@ -40,7 +35,7 @@ export const CopyButton = ({ text, style = {}, ...buttonProps }: CopyButtonProps
4035
onClick={handleCopy}
4136
{...buttonProps}
4237
>
43-
{isCopied ? t('CopyButton.copiedMessage') : text}
38+
{isCopied ? t('common.copyToClipboardSuccessToast') : text}
4439
</Button>
4540
);
4641
};

src/components/Yaml/YamlViewer.tsx

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,14 @@ import { materialLight, materialDark } from 'react-syntax-highlighter/dist/esm/s
44

55
import { Button, FlexBox } from '@ui5/webcomponents-react';
66
import styles from './YamlViewer.module.css';
7-
import { useToast } from '../../context/ToastContext.tsx';
7+
import { useCopyToClipboard } from '../../hooks/useCopyToClipboard.ts';
88
import { useTranslation } from 'react-i18next';
99
import { useTheme } from '../../hooks/useTheme.ts';
1010
type YamlViewerProps = { yamlString: string; filename: string };
1111
const YamlViewer: FC<YamlViewerProps> = ({ yamlString, filename }) => {
12-
const toast = useToast();
1312
const { t } = useTranslation();
1413
const { isDarkTheme } = useTheme();
15-
const copyToClipboard = () => {
16-
navigator.clipboard.writeText(yamlString);
17-
toast.show(t('yaml.copiedToClipboard'));
18-
};
14+
const { copyToClipboard } = useCopyToClipboard();
1915
const downloadYaml = () => {
2016
const blob = new Blob([yamlString], { type: 'text/yaml' });
2117
const url = window.URL.createObjectURL(blob);
@@ -31,7 +27,7 @@ const YamlViewer: FC<YamlViewerProps> = ({ yamlString, filename }) => {
3127
return (
3228
<div className={styles.container}>
3329
<FlexBox className={styles.buttons} direction="Row" justifyContent="End" alignItems="Baseline" gap={16}>
34-
<Button icon="copy" onClick={copyToClipboard}>
30+
<Button icon="copy" onClick={() => copyToClipboard(yamlString)}>
3531
{t('buttons.copy')}
3632
</Button>
3733
<Button icon="download" onClick={downloadYaml}>

src/hooks/useCopyToClipboard.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { useCallback } from 'react';
2+
import { useToast } from '../context/ToastContext.tsx';
3+
import { useTranslation } from 'react-i18next';
4+
5+
export type CopyFn = (text: string, showToastOnSuccess?: boolean) => Promise<boolean>;
6+
7+
export function useCopyToClipboard(): { copyToClipboard: CopyFn } {
8+
const toast = useToast();
9+
const { t } = useTranslation();
10+
11+
const copyToClipboard: CopyFn = useCallback(
12+
async (text, showToastOnSuccess = true) => {
13+
if (!navigator?.clipboard) {
14+
toast.show(t('common.copyToClipboardFailedToast'));
15+
return false;
16+
}
17+
18+
try {
19+
await navigator.clipboard.writeText(text);
20+
if (showToastOnSuccess) {
21+
toast.show(t('common.copyToClipboardSuccessToast'));
22+
}
23+
return true;
24+
} catch (error) {
25+
toast.show(t('common.copyToClipboardFailedToast'));
26+
console.error(error);
27+
return false;
28+
}
29+
},
30+
[toast, t],
31+
);
32+
33+
return { copyToClipboard };
34+
}

0 commit comments

Comments
 (0)