Skip to content

Commit bfccf4a

Browse files
committed
feat: change copy button to show copied state after click with auto-revert; closes #98
1 parent cf874ec commit bfccf4a

File tree

1 file changed

+155
-91
lines changed

1 file changed

+155
-91
lines changed

src/pages/UpdatedDatasetDetailPage.tsx

Lines changed: 155 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import PreviewModal from "../components/PreviewModal";
2+
import CheckIcon from "@mui/icons-material/Check";
23
import CloudDownloadIcon from "@mui/icons-material/CloudDownload";
34
import ContentCopyIcon from "@mui/icons-material/ContentCopy";
45
import DescriptionIcon from "@mui/icons-material/Description";
@@ -14,6 +15,7 @@ import {
1415
Button,
1516
Collapse,
1617
Tooltip,
18+
IconButton,
1719
} from "@mui/material";
1820
import FileTree from "components/DatasetDetailPage/FileTree/FileTree";
1921
import {
@@ -26,7 +28,7 @@ import ReadMoreText from "design/ReadMoreText";
2628
import { Colors } from "design/theme";
2729
import { useAppDispatch } from "hooks/useAppDispatch";
2830
import { useAppSelector } from "hooks/useAppSelector";
29-
import React, { useEffect, useMemo, useState } from "react";
31+
import React, { useEffect, useMemo, useState, useRef } from "react";
3032
// import ReactJson from "react-json-view";
3133
import { useParams, useNavigate, useSearchParams } from "react-router-dom";
3234
import {
@@ -79,13 +81,16 @@ const UpdatedDatasetDetailPage: React.FC = () => {
7981
const [jsonSize, setJsonSize] = useState<number>(0);
8082
const [previewIndex, setPreviewIndex] = useState<number>(0);
8183
const [isPreviewLoading, setIsPreviewLoading] = useState(false);
82-
const [copiedToast, setCopiedToast] = useState<{
83-
open: boolean;
84-
text: string;
85-
}>({
86-
open: false,
87-
text: "",
88-
});
84+
// const [copiedToast, setCopiedToast] = useState<{
85+
// open: boolean;
86+
// text: string;
87+
// }>({
88+
// open: false,
89+
// text: "",
90+
// });
91+
// const [copiedUrlOpen, setCopiedUrlOpen] = useState(false);
92+
const [copiedKey, setCopiedKey] = useState<string | null>(null);
93+
const copyTimer = useRef<number | null>(null);
8994
const aiSummary = datasetDocument?.[".datainfo"]?.AISummary ?? "";
9095
const handleSelectRevision = (newRev?: string | null) => {
9196
setSearchParams((prev) => {
@@ -263,7 +268,7 @@ const UpdatedDatasetDetailPage: React.FC = () => {
263268
const url = buildPreviewUrl(path);
264269
try {
265270
await navigator.clipboard.writeText(url);
266-
setCopiedToast({ open: true, text: "Preview link copied" });
271+
// setCopiedToast({ open: true, text: "Preview link copied" });
267272
} catch {
268273
// fallback
269274
const ta = document.createElement("textarea");
@@ -272,20 +277,31 @@ const UpdatedDatasetDetailPage: React.FC = () => {
272277
ta.select();
273278
document.execCommand("copy");
274279
document.body.removeChild(ta);
275-
setCopiedToast({ open: true, text: "Preview link copied" });
280+
// setCopiedToast({ open: true, text: "Preview link copied" });
276281
}
277282
};
278283

279-
// useEffect(() => {
280-
// const fetchData = async () => {
281-
// if (dbName && docId) {
282-
// await dispatch(fetchDocumentDetails({ dbName, docId }));
283-
// await dispatch(fetchDbInfoByDatasetId({ dbName, docId }));
284-
// }
285-
// };
284+
// const handleUrlCopyClick = async (e: React.MouseEvent, path: string) => {
285+
// await copyPreviewUrl(path);
286+
// setCopiedUrlOpen(true);
287+
// setTimeout(() => setCopiedUrlOpen(false), 2500);
288+
// };
286289

287-
// fetchData();
288-
// }, [dbName, docId, dispatch]);
290+
const handleUrlCopyClick = async (
291+
e: React.MouseEvent<HTMLButtonElement>,
292+
path: string
293+
) => {
294+
await copyPreviewUrl(path);
295+
setCopiedKey(path); // mark this button as "copied"
296+
if (copyTimer.current) clearTimeout(copyTimer.current);
297+
copyTimer.current = window.setTimeout(() => setCopiedKey(null), 1500);
298+
};
299+
300+
React.useEffect(() => {
301+
return () => {
302+
if (copyTimer.current) clearTimeout(copyTimer.current);
303+
};
304+
}, []);
289305

290306
useEffect(() => {
291307
if (!dbName || !docId) return;
@@ -990,83 +1006,109 @@ const UpdatedDatasetDetailPage: React.FC = () => {
9901006
}}
9911007
>
9921008
{internalLinks.length > 0 ? (
993-
internalLinks.map((link, index) => (
994-
<Box
995-
key={index}
996-
sx={{
997-
display: "flex",
998-
alignItems: "center",
999-
justifyContent: "space-between",
1000-
padding: "6px 10px",
1001-
backgroundColor: "white",
1002-
borderRadius: "4px",
1003-
border: "1px solid #ddd",
1004-
mt: 1,
1005-
height: "34px",
1006-
minWidth: 0,
1007-
fontSize: "0.85rem",
1008-
}}
1009-
>
1010-
<Typography
1009+
internalLinks.map((link, index) => {
1010+
const key = link.path;
1011+
return (
1012+
<Box
1013+
key={index}
10111014
sx={{
1012-
flexGrow: 1,
1015+
display: "flex",
1016+
alignItems: "center",
1017+
justifyContent: "space-between",
1018+
padding: "6px 10px",
1019+
backgroundColor: "white",
1020+
borderRadius: "4px",
1021+
border: "1px solid #ddd",
1022+
mt: 1,
1023+
height: "34px",
10131024
minWidth: 0,
1014-
whiteSpace: "nowrap",
1015-
overflow: "hidden",
1016-
textOverflow: "ellipsis",
1017-
fontSize: "1rem",
1018-
marginRight: "12px",
1019-
maxWidth: "calc(100% - 160px)",
1025+
fontSize: "0.85rem",
10201026
}}
1021-
title={link.name}
10221027
>
1023-
{link.name}{" "}
1024-
{link.arraySize ? `[${link.arraySize.join("x")}]` : ""}
1025-
</Typography>
1026-
<Box sx={{ display: "flex", flexShrink: 0, gap: 1 }}>
1027-
<Button
1028-
variant="contained"
1029-
size="small"
1030-
sx={{
1031-
backgroundColor: Colors.purple,
1032-
flexShrink: 0,
1033-
minWidth: "70px",
1034-
fontSize: "0.7rem",
1035-
padding: "2px 6px",
1036-
lineHeight: 1,
1037-
"&:hover": {
1038-
backgroundColor: Colors.secondaryPurple,
1039-
},
1040-
}}
1041-
onClick={() =>
1042-
handlePreview(link.data, link.index, true)
1043-
}
1044-
>
1045-
Preview
1046-
</Button>
1047-
<Button
1048-
variant="outlined"
1049-
size="small"
1028+
<Typography
10501029
sx={{
1051-
color: Colors.purple,
1052-
borderColor: Colors.purple,
1053-
minWidth: "90px",
1054-
fontSize: "0.7rem",
1055-
padding: "2px 6px",
1056-
lineHeight: 1,
1057-
"&:hover": {
1058-
color: Colors.secondaryPurple,
1059-
borderColor: Colors.secondaryPurple,
1060-
},
1030+
flexGrow: 1,
1031+
minWidth: 0,
1032+
whiteSpace: "nowrap",
1033+
overflow: "hidden",
1034+
textOverflow: "ellipsis",
1035+
fontSize: "1rem",
1036+
marginRight: "12px",
1037+
maxWidth: "calc(100% - 160px)",
10611038
}}
1062-
onClick={() => copyPreviewUrl(link.path)}
1039+
title={link.name}
10631040
>
1064-
<ContentCopyIcon sx={{ fontSize: 16, mr: 0.5 }} />
1065-
Copy URL
1066-
</Button>
1041+
{link.name}{" "}
1042+
{link.arraySize
1043+
? `[${link.arraySize.join("x")}]`
1044+
: ""}
1045+
</Typography>
1046+
<Box sx={{ display: "flex", flexShrink: 0, gap: 1 }}>
1047+
<Button
1048+
variant="contained"
1049+
size="small"
1050+
sx={{
1051+
backgroundColor: Colors.purple,
1052+
flexShrink: 0,
1053+
minWidth: "70px",
1054+
fontSize: "0.7rem",
1055+
padding: "2px 6px",
1056+
lineHeight: 1,
1057+
"&:hover": {
1058+
backgroundColor: Colors.secondaryPurple,
1059+
},
1060+
}}
1061+
onClick={() =>
1062+
handlePreview(link.data, link.index, true)
1063+
}
1064+
>
1065+
Preview
1066+
</Button>
1067+
{/* <Tooltip
1068+
title="Copied!"
1069+
open={copiedUrlOpen}
1070+
disableHoverListener
1071+
> */}
1072+
<Button
1073+
variant="outlined"
1074+
size="small"
1075+
sx={{
1076+
color: Colors.purple,
1077+
borderColor: Colors.purple,
1078+
minWidth: "90px",
1079+
fontSize: "0.7rem",
1080+
padding: "2px 6px",
1081+
lineHeight: 1,
1082+
"&:hover": {
1083+
color: Colors.secondaryPurple,
1084+
borderColor: Colors.secondaryPurple,
1085+
},
1086+
}}
1087+
// onClick={() => copyPreviewUrl(link.path)}
1088+
onClick={(e) => handleUrlCopyClick(e, key)}
1089+
disabled={copiedKey === key}
1090+
>
1091+
{copiedKey === key ? (
1092+
<>
1093+
<CheckIcon sx={{ fontSize: 16, mr: 0.5 }} />
1094+
Copied!
1095+
</>
1096+
) : (
1097+
<>
1098+
<ContentCopyIcon
1099+
sx={{ fontSize: 16, mr: 0.5 }}
1100+
/>
1101+
Copy URL
1102+
</>
1103+
)}
1104+
{/* <ContentCopyIcon sx={{ fontSize: 16, mr: 0.5 }} />
1105+
Copy URL */}
1106+
</Button>
1107+
{/* </Tooltip> */}
1108+
</Box>
10671109
</Box>
1068-
</Box>
1069-
))
1110+
);
1111+
})
10701112
) : (
10711113
<Typography sx={{ fontStyle: "italic", mt: 1 }}>
10721114
No internal data found.
@@ -1120,6 +1162,7 @@ const UpdatedDatasetDetailPage: React.FC = () => {
11201162
>
11211163
{externalLinks.length > 0 ? (
11221164
externalLinks.map((link, index) => {
1165+
const key = link.path;
11231166
const match = link.url.match(/file=([^&]+)/);
11241167
const fileName = match ? match[1] : "";
11251168
const isPreviewable =
@@ -1199,6 +1242,11 @@ const UpdatedDatasetDetailPage: React.FC = () => {
11991242
</Button>
12001243
)}
12011244
{isPreviewable && (
1245+
// <Tooltip
1246+
// title="Copied!"
1247+
// open={copiedUrlOpen}
1248+
// disableHoverListener
1249+
// >
12021250
<Button
12031251
variant="outlined"
12041252
size="small"
@@ -1214,11 +1262,27 @@ const UpdatedDatasetDetailPage: React.FC = () => {
12141262
borderColor: Colors.secondaryPurple,
12151263
},
12161264
}}
1217-
onClick={() => copyPreviewUrl(link.path)} // <-- use the JSON path
1265+
onClick={(e) => handleUrlCopyClick(e, key)}
1266+
disabled={copiedKey === key}
1267+
// onClick={() => copyPreviewUrl(link.path)} // <-- use the JSON path
12181268
>
1219-
<ContentCopyIcon sx={{ fontSize: 16, mr: 0.5 }} />
1220-
Copy URL
1269+
{copiedKey === key ? (
1270+
<>
1271+
<CheckIcon sx={{ fontSize: 16, mr: 0.5 }} />
1272+
Copied!
1273+
</>
1274+
) : (
1275+
<>
1276+
<ContentCopyIcon
1277+
sx={{ fontSize: 16, mr: 0.5 }}
1278+
/>
1279+
Copy URL
1280+
</>
1281+
)}
1282+
{/* <ContentCopyIcon sx={{ fontSize: 16, mr: 0.5 }} />
1283+
Copy URL */}
12211284
</Button>
1285+
// </Tooltip>
12221286
)}
12231287
</Box>
12241288
</Box>

0 commit comments

Comments
 (0)