diff --git a/packages/bruno-app/src/components/ResponsePane/ResponseActions/StyledWrapper.js b/packages/bruno-app/src/components/ResponsePane/ResponseActions/StyledWrapper.js
new file mode 100644
index 000000000..6ba69090e
--- /dev/null
+++ b/packages/bruno-app/src/components/ResponsePane/ResponseActions/StyledWrapper.js
@@ -0,0 +1,29 @@
+import styled from 'styled-components';
+
+const StyledWrapper = styled.div`
+ button {
+ color: var(--color-tab-inactive);
+ cursor: pointer;
+
+ &:hover {
+ color: var(--color-tab-active);
+ }
+
+ &:disabled {
+ cursor: not-allowed;
+ opacity: 0.5;
+ }
+ }
+
+ .cursor-pointer {
+ display: flex;
+ align-items: center;
+ color: var(--color-tab-inactive);
+
+ &:hover {
+ color: var(--color-tab-active);
+ }
+ }
+`;
+
+export default StyledWrapper;
diff --git a/packages/bruno-app/src/components/ResponsePane/ResponseActions/index.js b/packages/bruno-app/src/components/ResponsePane/ResponseActions/index.js
new file mode 100644
index 000000000..a430153a0
--- /dev/null
+++ b/packages/bruno-app/src/components/ResponsePane/ResponseActions/index.js
@@ -0,0 +1,35 @@
+import React, { useRef, forwardRef } from 'react';
+import { IconDots } from '@tabler/icons';
+import Dropdown from 'components/Dropdown';
+import StyledWrapper from './StyledWrapper';
+import ResponseClear from 'src/components/ResponsePane/ResponseClear';
+import ResponseSave from 'src/components/ResponsePane/ResponseSave';
+
+const ResponseActions = ({ collection, item }) => {
+ const menuDropdownTippyRef = useRef();
+
+ const onMenuDropdownCreate = (ref) => (menuDropdownTippyRef.current = ref);
+
+ const MenuIcon = forwardRef((_props, ref) => {
+ return (
+
+
+
+ );
+ });
+
+ const handleClose = () => {
+ menuDropdownTippyRef.current.hide();
+ };
+
+ return (
+
+ } placement="bottom-end">
+
+
+
+
+ );
+};
+
+export default ResponseActions;
diff --git a/packages/bruno-app/src/components/ResponsePane/ResponseClear/index.js b/packages/bruno-app/src/components/ResponsePane/ResponseClear/index.js
index 747543347..b18418592 100644
--- a/packages/bruno-app/src/components/ResponsePane/ResponseClear/index.js
+++ b/packages/bruno-app/src/components/ResponsePane/ResponseClear/index.js
@@ -4,10 +4,11 @@ import { useDispatch } from 'react-redux';
import StyledWrapper from './StyledWrapper';
import { responseCleared } from 'providers/ReduxStore/slices/collections/index';
-const ResponseClear = ({ collection, item }) => {
+const ResponseClear = ({ collection, item, asDropdownItem, onClose }) => {
const dispatch = useDispatch();
- const clearResponse = () =>
+ const clearResponse = () => {
+ if (onClose) onClose();
dispatch(
responseCleared({
itemUid: item.uid,
@@ -15,6 +16,16 @@ const ResponseClear = ({ collection, item }) => {
response: null
})
);
+ };
+
+ if (asDropdownItem) {
+ return (
+
+
+ Clear
+
+ );
+ }
return (
diff --git a/packages/bruno-app/src/components/ResponsePane/ResponseCopy/StyledWrapper.js b/packages/bruno-app/src/components/ResponsePane/ResponseCopy/StyledWrapper.js
index b2acb52b8..8c32a8bab 100644
--- a/packages/bruno-app/src/components/ResponsePane/ResponseCopy/StyledWrapper.js
+++ b/packages/bruno-app/src/components/ResponsePane/ResponseCopy/StyledWrapper.js
@@ -5,4 +5,4 @@ const StyledWrapper = styled.div`
color: ${(props) => props.theme.requestTabPanel.responseStatus};
`;
-export default StyledWrapper;
\ No newline at end of file
+export default StyledWrapper;
diff --git a/packages/bruno-app/src/components/ResponsePane/ResponseCopy/index.js b/packages/bruno-app/src/components/ResponsePane/ResponseCopy/index.js
index 80763f131..7c59301ef 100644
--- a/packages/bruno-app/src/components/ResponsePane/ResponseCopy/index.js
+++ b/packages/bruno-app/src/components/ResponsePane/ResponseCopy/index.js
@@ -1,33 +1,46 @@
-import React from 'react';
+import React, { useState, useEffect } from 'react';
import StyledWrapper from './StyledWrapper';
import toast from 'react-hot-toast';
-import { IconCopy } from '@tabler/icons';
+import { IconCopy, IconCheck } from '@tabler/icons';
const ResponseCopy = ({ item }) => {
const response = item.response || {};
+ const [copied, setCopied] = useState(false);
- const copyResponse = () => {
+ useEffect(() => {
+ if (copied) {
+ const timer = setTimeout(() => {
+ setCopied(false);
+ }, 2000);
+ return () => clearTimeout(timer);
+ }
+ }, [copied]);
+
+ const copyResponse = async () => {
try {
- const textToCopy = typeof response.data === 'string'
- ? response.data
- : JSON.stringify(response.data, null, 2);
+ const textToCopy = typeof response.data === 'string'
+ ? response.data
+ : JSON.stringify(response.data, null, 2);
- navigator.clipboard.writeText(textToCopy).then(() => {
+ await navigator.clipboard.writeText(textToCopy);
toast.success('Response copied to clipboard');
- }).catch(() => {
- toast.error('Failed to copy response');
- });
+ setCopied(true);
} catch (error) {
- toast.error('Failed to copy response');
+ toast.error('Failed to copy response');
}
};
return (
);
};
-export default ResponseCopy;
\ No newline at end of file
+
+export default ResponseCopy;
diff --git a/packages/bruno-app/src/components/ResponsePane/ResponseSave/index.js b/packages/bruno-app/src/components/ResponsePane/ResponseSave/index.js
index 15eef651b..c791463cd 100644
--- a/packages/bruno-app/src/components/ResponsePane/ResponseSave/index.js
+++ b/packages/bruno-app/src/components/ResponsePane/ResponseSave/index.js
@@ -4,11 +4,13 @@ import toast from 'react-hot-toast';
import get from 'lodash/get';
import { IconDownload } from '@tabler/icons';
-const ResponseSave = ({ item }) => {
+const ResponseSave = ({ item, asDropdownItem, onClose }) => {
const { ipcRenderer } = window;
const response = item.response || {};
const saveResponseToFile = () => {
+ if (!response.dataBuffer) return;
+ if (onClose) onClose();
return new Promise((resolve, reject) => {
ipcRenderer
.invoke('renderer:save-response-to-file', response, item?.requestSent?.url, item.pathname)
@@ -20,6 +22,20 @@ const ResponseSave = ({ item }) => {
});
};
+ if (asDropdownItem) {
+ return (
+
+
+ Download
+
+ );
+ }
+
return (