Repository: provectus/kafka-ui
Issue: [Feature] Feedback to the user when a message content is copied to clipboard
Labels: type/enhancement
good first issue
scope/frontend
status/accepted

When I copy a message to my clipboard, there is no clear indications that it is done. 
Obviously, I can paste it wherever I want.

But I'd find nice to have some notifications anywhere that would say me "message copied to clipboard" then disappears some seconds later.

Version tested : ([8a7399b](https://github.com/provectus/kafka-ui/commit/8a7399b))

Issue comments:
Hey, thanks for the suggestion. Definitely will do :)

Pull request review comments:
Put this outside of hook, you need to define this constant only once
Also, correct the spelling of word DISSMISS
it's not a good idea to name a variable 'test',change the name please
Typo
```suggestion
const AUTO_DISMISS_TIME = 2000;
```
Cool 
I will use this in my PR
Move dispatch outside copyToClipboard, the hooks should be called only in other hook/component 1st level scope

Patch that resolved the issue:
diff --git a/kafka-ui-react-app/src/lib/hooks/__tests__/useDataSaver.spec.tsx b/kafka-ui-react-app/src/lib/hooks/__tests__/useDataSaver.spec.tsx
index 85a06d3ca5e..bc2cc9b66ce 100644
--- a/kafka-ui-react-app/src/lib/hooks/__tests__/useDataSaver.spec.tsx
+++ b/kafka-ui-react-app/src/lib/hooks/__tests__/useDataSaver.spec.tsx
@@ -1,9 +1,12 @@
+import React, { useEffect } from 'react';
+import { render } from 'lib/testHelpers';
 import useDataSaver from 'lib/hooks/useDataSaver';
 
 describe('useDataSaver hook', () => {
   const content = {
     title: 'title',
   };
+
   describe('Save as file', () => {
     beforeAll(() => {
       jest.useFakeTimers('modern');
@@ -20,10 +23,14 @@ describe('useDataSaver hook', () => {
         .spyOn(document, 'createElement')
         .mockImplementation(() => link);
 
-      const { saveFile } = useDataSaver('message', content);
-      saveFile();
+      const HookWrapper: React.FC = () => {
+        const { saveFile } = useDataSaver('message', content);
+        useEffect(() => saveFile(), []);
+        return null;
+      };
 
-      expect(mockCreate).toHaveBeenCalledTimes(1);
+      render(<HookWrapper />);
+      expect(mockCreate).toHaveBeenCalledTimes(2);
       expect(link.download).toEqual('message_1616581196000.json');
       expect(link.href).toEqual(
         `data:text/json;charset=utf-8,${encodeURIComponent(
@@ -43,10 +50,14 @@ describe('useDataSaver hook', () => {
         .spyOn(document, 'createElement')
         .mockImplementation(() => link);
 
-      const { saveFile } = useDataSaver('message', 'content');
-      saveFile();
+      const HookWrapper: React.FC = () => {
+        const { saveFile } = useDataSaver('message', 'content');
+        useEffect(() => saveFile(), []);
+        return null;
+      };
 
-      expect(mockCreate).toHaveBeenCalledTimes(1);
+      render(<HookWrapper />);
+      expect(mockCreate).toHaveBeenCalledTimes(2);
       expect(link.download).toEqual('message_1616581196000.txt');
       expect(link.href).toEqual(
         `data:text/json;charset=utf-8,${encodeURIComponent(
@@ -66,9 +77,14 @@ describe('useDataSaver hook', () => {
       },
     });
     jest.spyOn(navigator.clipboard, 'writeText');
-    const { copyToClipboard } = useDataSaver('topic', content);
-    copyToClipboard();
 
+    const HookWrapper: React.FC = () => {
+      const { copyToClipboard } = useDataSaver('topic', content);
+      useEffect(() => copyToClipboard(), []);
+      return null;
+    };
+
+    render(<HookWrapper />);
     expect(navigator.clipboard.writeText).toHaveBeenCalledWith(
       JSON.stringify(content)
     );
diff --git a/kafka-ui-react-app/src/lib/hooks/useDataSaver.ts b/kafka-ui-react-app/src/lib/hooks/useDataSaver.ts
index b2c57ea7703..25c42602f12 100644
--- a/kafka-ui-react-app/src/lib/hooks/useDataSaver.ts
+++ b/kafka-ui-react-app/src/lib/hooks/useDataSaver.ts
@@ -1,13 +1,28 @@
 import { isObject } from 'lodash';
+import { alertAdded, alertDissmissed } from 'redux/reducers/alerts/alertsSlice';
+import { useAppDispatch } from 'lib/hooks/redux';
+
+const AUTO_DISMISS_TIME = 2000;
 
 const useDataSaver = (
   subject: string,
   data: Record<string, string> | string
 ) => {
+  const dispatch = useAppDispatch();
   const copyToClipboard = () => {
     if (navigator.clipboard) {
       const str = JSON.stringify(data);
       navigator.clipboard.writeText(str);
+      dispatch(
+        alertAdded({
+          id: subject,
+          type: 'success',
+          title: '',
+          message: 'Copied successfully!',
+          createdAt: Date.now(),
+        })
+      );
+      setTimeout(() => dispatch(alertDissmissed(subject)), AUTO_DISMISS_TIME);
     }
   };
 


Test patch: