@bindu-dashing/dam-solution-v2 5.9.251 → 5.9.252
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -12,6 +12,7 @@ import { useState } from "react";
|
|
|
12
12
|
import { useDamConfig } from "../hocs/DamConfigContext";
|
|
13
13
|
import EditImageModal from "./ImageEditor/EditImageModal";
|
|
14
14
|
import EditPdfModal from "./PdfEditor/EditPdfModal";
|
|
15
|
+
import { withVersion } from "../utilities/helpers/cacheBust";
|
|
15
16
|
const IconInprogress = GrInProgress;
|
|
16
17
|
const AiOutlineEditIcon = AiOutlineEdit;
|
|
17
18
|
const AiOutlineCheckCircleIcon = AiOutlineCheckCircle;
|
|
@@ -51,7 +52,7 @@ const FilesGridView = ({ files, handleClick, selectedFileIds, isImagePicker, sel
|
|
|
51
52
|
openEditor(file);
|
|
52
53
|
}, children: _jsx(AiOutlineEditIcon, { className: "md-lib-text-[18px] md-lib-text-textColor dark:md-lib-text-darkTextColor hover:md-lib-text-primary md-lib-cursor-pointer" }) }) }))] }), _jsx("div", { className: "md-lib-mt-2 md-lib-h-28 md-lib-mx-2.5 md-lib-bg-white dark:md-lib-bg-darkPrimaryHoverColor md-lib-rounded-md md-lib-flex md-lib-justify-center", children: get(file, "fileUploadStatus") === ThumbnailStatus.PENDING ? (
|
|
53
54
|
// <IconInprogress className="mt-10 text-[40px] text-imagesColor" />
|
|
54
|
-
_jsx(Image, { src: UPLOAD_FILE_IN_PROGRESS_WHITE_IMAGE, width: 100, height: 100 })) : (_jsx("img", { src: get(file, "thumbnailUrl", ""), alt: get(file, "name", "N/A"), className: "md-lib-w-full md-lib-h-full md-lib-object-contain md-lib-p-1" })) }), _jsx("div", { children: _jsx("p", { className: "md-lib-text-xs md-lib-text-textColor dark:md-lib-text-darkTextColor md-lib-px-5 md-lib-pt-2.5 md-lib-truncate md-lib-max-w-40", title: get(file, "name", "N/A"), children: get(file, "assetName", "N/A") }) })] }, get(file, "_id")));
|
|
55
|
+
_jsx(Image, { src: UPLOAD_FILE_IN_PROGRESS_WHITE_IMAGE, width: 100, height: 100 })) : (_jsx("img", { src: withVersion(get(file, "thumbnailUrl", ""), get(file, "updatedAt")), alt: get(file, "name", "N/A"), className: "md-lib-w-full md-lib-h-full md-lib-object-contain md-lib-p-1" })) }), _jsx("div", { children: _jsx("p", { className: "md-lib-text-xs md-lib-text-textColor dark:md-lib-text-darkTextColor md-lib-px-5 md-lib-pt-2.5 md-lib-truncate md-lib-max-w-40", title: get(file, "name", "N/A"), children: get(file, "assetName", "N/A") }) })] }, get(file, "_id")));
|
|
55
56
|
}) }), showEditModal && editFile && getEditorType(editFile) === "image" && (_jsx(EditImageModal, { open: showEditModal, handleClose: closeEditor, file: editFile })), showEditModal && editFile && getEditorType(editFile) === "pdf" && (_jsx(EditPdfModal, { open: showEditModal, handleClose: closeEditor, file: editFile }))] }));
|
|
56
57
|
};
|
|
57
58
|
export default FilesGridView;
|
|
@@ -9,6 +9,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
9
9
|
});
|
|
10
10
|
};
|
|
11
11
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
12
|
+
import { Button, Modal } from "antd";
|
|
12
13
|
import { GoArrowLeft } from "react-icons/go";
|
|
13
14
|
import { PiFloppyDisk } from "react-icons/pi";
|
|
14
15
|
import TuiImageEditor from "./TuiImageEditor";
|
|
@@ -18,7 +19,7 @@ import { get } from "lodash";
|
|
|
18
19
|
import CustomButton from "../../common/Button";
|
|
19
20
|
import { showNotification } from "../../common/notifications";
|
|
20
21
|
import { FILE_UPLOAD_ERROR, SOMETHING_WENT_WRONG, UPDATE_SUCCESS, } from "../../utilities/constants/messages";
|
|
21
|
-
import { SAVE_EDITED_FILE_THUMBNAIL_URL, SAVE_EDITED_FILE_URL, } from "../../utilities/constants/apiUrls";
|
|
22
|
+
import { FILE_UPLOAD_URL, SAVE_EDITED_FILE_THUMBNAIL_URL, SAVE_EDITED_FILE_URL, } from "../../utilities/constants/apiUrls";
|
|
22
23
|
import { generateFoldersQueryKey, invalidateData, QueryKeys, } from "../../utilities/constants/queryKeys";
|
|
23
24
|
import { useQueryClient } from "react-query";
|
|
24
25
|
import { useDamConfig } from "../../hocs/DamConfigContext";
|
|
@@ -26,77 +27,123 @@ import { createApiClient } from "../../hocs/configureAxios";
|
|
|
26
27
|
import useAppParams from "../../utilities/useAppParams";
|
|
27
28
|
const GoArrowLeftIcon = GoArrowLeft;
|
|
28
29
|
const PiFloppyDiskIcon = PiFloppyDisk;
|
|
30
|
+
const buildEditedName = (originalName) => {
|
|
31
|
+
const dot = originalName.lastIndexOf(".");
|
|
32
|
+
if (dot <= 0)
|
|
33
|
+
return `${originalName} (edited)`;
|
|
34
|
+
return `${originalName.slice(0, dot)} (edited)${originalName.slice(dot)}`;
|
|
35
|
+
};
|
|
29
36
|
const ImageEditorComponent = ({ file, handleClose, }) => {
|
|
30
37
|
const queryClient = useQueryClient();
|
|
31
38
|
const damConfig = useDamConfig();
|
|
32
|
-
const { rootFolderId } = damConfig;
|
|
39
|
+
const { rootFolderId, brand } = damConfig;
|
|
40
|
+
const brandId = get(brand, "_id");
|
|
33
41
|
const api = useMemo(() => createApiClient(damConfig), [damConfig]);
|
|
34
42
|
const { type, folderId } = useAppParams();
|
|
43
|
+
const currentFolderId = folderId || rootFolderId;
|
|
35
44
|
const [state, setState] = useState({
|
|
36
45
|
loading: false,
|
|
46
|
+
confirmOpen: false,
|
|
37
47
|
});
|
|
38
|
-
const { loading } = state;
|
|
48
|
+
const { loading, confirmOpen } = state;
|
|
39
49
|
const editorRef = useRef(null);
|
|
40
|
-
const
|
|
50
|
+
const invalidateAfterChange = () => {
|
|
51
|
+
invalidateData(queryClient, generateFoldersQueryKey(type), currentFolderId);
|
|
52
|
+
invalidateData(queryClient, QueryKeys.FILE, get(file, "_id"));
|
|
53
|
+
};
|
|
54
|
+
const handleOverwrite = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
41
55
|
var _a;
|
|
42
56
|
const editedFile = yield ((_a = editorRef.current) === null || _a === void 0 ? void 0 : _a.getEditedFile());
|
|
43
|
-
if (editedFile) {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
finally {
|
|
73
|
-
setState((prevState) => (Object.assign(Object.assign({}, prevState), { loading: false })));
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
else {
|
|
77
|
-
showNotification(FILE_UPLOAD_ERROR, NotificationStatus.ERROR);
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
catch (error) {
|
|
81
|
-
showNotification(get(error, "message", FILE_UPLOAD_ERROR), NotificationStatus.ERROR);
|
|
82
|
-
}
|
|
83
|
-
finally {
|
|
84
|
-
setState((prevState) => (Object.assign(Object.assign({}, prevState), { loading: false })));
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
else {
|
|
88
|
-
showNotification(FILE_UPLOAD_ERROR, NotificationStatus.ERROR);
|
|
89
|
-
}
|
|
57
|
+
if (!editedFile) {
|
|
58
|
+
showNotification("Error while saving image", NotificationStatus.ERROR);
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
setState((prev) => (Object.assign(Object.assign({}, prev), { loading: true, confirmOpen: false })));
|
|
62
|
+
try {
|
|
63
|
+
const response = yield api.put(SAVE_EDITED_FILE_URL, {
|
|
64
|
+
path: get(file, "s3Path"),
|
|
65
|
+
});
|
|
66
|
+
const url = get(response, "data");
|
|
67
|
+
if (!url) {
|
|
68
|
+
showNotification(FILE_UPLOAD_ERROR, NotificationStatus.ERROR);
|
|
69
|
+
setState((prev) => (Object.assign(Object.assign({}, prev), { loading: false })));
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
// Use fetch instead of axios to avoid inheriting Authorization header
|
|
73
|
+
// from host app - S3 presigned URLs reject requests with Bearer tokens
|
|
74
|
+
const s3Resp = yield fetch(url, {
|
|
75
|
+
method: "PUT",
|
|
76
|
+
headers: {
|
|
77
|
+
"Content-Type": editedFile.type || "image/png",
|
|
78
|
+
},
|
|
79
|
+
body: editedFile,
|
|
80
|
+
});
|
|
81
|
+
if (s3Resp.ok) {
|
|
82
|
+
yield api.put(SAVE_EDITED_FILE_THUMBNAIL_URL.replace(":fileId", get(file, "_id")), {});
|
|
83
|
+
invalidateAfterChange();
|
|
84
|
+
showNotification(UPDATE_SUCCESS, NotificationStatus.SUCCESS);
|
|
85
|
+
handleClose();
|
|
90
86
|
}
|
|
91
|
-
|
|
92
|
-
showNotification(
|
|
93
|
-
setState((prevState) => (Object.assign(Object.assign({}, prevState), { loading: false })));
|
|
87
|
+
else {
|
|
88
|
+
showNotification(FILE_UPLOAD_ERROR, NotificationStatus.ERROR);
|
|
94
89
|
}
|
|
95
90
|
}
|
|
96
|
-
|
|
91
|
+
catch (error) {
|
|
92
|
+
showNotification(get(error, "message", SOMETHING_WENT_WRONG), NotificationStatus.ERROR);
|
|
93
|
+
}
|
|
94
|
+
finally {
|
|
95
|
+
setState((prev) => (Object.assign(Object.assign({}, prev), { loading: false })));
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
const handleSaveAsNew = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
99
|
+
var _a;
|
|
100
|
+
const editedFile = yield ((_a = editorRef.current) === null || _a === void 0 ? void 0 : _a.getEditedFile());
|
|
101
|
+
if (!editedFile) {
|
|
97
102
|
showNotification("Error while saving image", NotificationStatus.ERROR);
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
setState((prev) => (Object.assign(Object.assign({}, prev), { loading: true, confirmOpen: false })));
|
|
106
|
+
const newName = buildEditedName(get(file, "name", "edited.png"));
|
|
107
|
+
const mimetype = editedFile.type || get(file, "mimetype") || "image/png";
|
|
108
|
+
try {
|
|
109
|
+
const presigned = yield api.post(FILE_UPLOAD_URL, {
|
|
110
|
+
brandId,
|
|
111
|
+
folderId: currentFolderId,
|
|
112
|
+
files: [{ name: newName, size: editedFile.size }],
|
|
113
|
+
});
|
|
114
|
+
const uploadUrl = get(presigned, ["data", 0, "url"]);
|
|
115
|
+
if (!uploadUrl) {
|
|
116
|
+
showNotification(FILE_UPLOAD_ERROR, NotificationStatus.ERROR);
|
|
117
|
+
setState((prev) => (Object.assign(Object.assign({}, prev), { loading: false })));
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
const s3Resp = yield fetch(uploadUrl, {
|
|
121
|
+
method: "PUT",
|
|
122
|
+
headers: { "Content-Type": mimetype },
|
|
123
|
+
body: editedFile,
|
|
124
|
+
});
|
|
125
|
+
if (s3Resp.ok) {
|
|
126
|
+
invalidateAfterChange();
|
|
127
|
+
showNotification(UPDATE_SUCCESS, NotificationStatus.SUCCESS);
|
|
128
|
+
handleClose();
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
showNotification(FILE_UPLOAD_ERROR, NotificationStatus.ERROR);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
catch (error) {
|
|
135
|
+
showNotification(get(error, "message", SOMETHING_WENT_WRONG), NotificationStatus.ERROR);
|
|
136
|
+
}
|
|
137
|
+
finally {
|
|
138
|
+
setState((prev) => (Object.assign(Object.assign({}, prev), { loading: false })));
|
|
98
139
|
}
|
|
99
140
|
});
|
|
100
|
-
|
|
141
|
+
const openConfirm = () => setState((prev) => (Object.assign(Object.assign({}, prev), { confirmOpen: true })));
|
|
142
|
+
const closeConfirm = () => setState((prev) => (Object.assign(Object.assign({}, prev), { confirmOpen: false })));
|
|
143
|
+
return (_jsxs("div", { className: "md-lib-flex md-lib-flex-col md-lib-h-screen", children: [_jsxs("div", { className: "md-lib-shrink-0 md-lib-bg-darkPrimaryBg md-lib-py-3 md-lib-px-5 md-lib-flex md-lib-justify-between md-lib-items-center md-lib-relative md-lib-z-[100]", children: [_jsxs("div", { className: "md-lib-flex md-lib-items-center md-lib-gap-5", children: [_jsx(GoArrowLeftIcon, { className: "md-lib-text-darkTextColor md-lib-cursor-pointer", size: 24, onClick: handleClose }), _jsx("h4", { className: "md-lib-font-semibold md-lib-text-darkTextColor md-lib-text-base md-lib-truncate md-lib-w-96", title: get(file, "name", "N/A"), children: get(file, "name", "N/A") })] }), _jsxs("div", { className: "md-lib-flex md-lib-items-center md-lib-gap-4", children: [_jsx(CustomButton, { size: "large", onClick: handleClose, label: "Cancel", type: "default", className: "md-lib-bg-secondaryColor md-lib-hover:bg-secondaryHoverColor md-lib-border-none" }), _jsx(CustomButton, { size: "large", type: "primary", icon: _jsx(PiFloppyDiskIcon, { size: 24 }), onClick: openConfirm, label: "Save", loading: loading })] })] }), _jsx("div", { className: "md-lib-flex-1 md-lib-overflow-hidden md-lib-relative", children: _jsx(TuiImageEditor, { ref: editorRef, file: file, path: get(file, "s3Path") }) }), _jsx(Modal, { open: confirmOpen, onCancel: closeConfirm, title: "Save edited image", maskClosable: !loading, closable: !loading, footer: [
|
|
144
|
+
_jsx(Button, { onClick: closeConfirm, disabled: loading, children: "Cancel" }, "cancel"),
|
|
145
|
+
_jsx(Button, { onClick: handleOverwrite, disabled: loading, children: "Overwrite" }, "overwrite"),
|
|
146
|
+
_jsx(Button, { type: "primary", onClick: handleSaveAsNew, loading: loading, children: "Save as new" }, "new"),
|
|
147
|
+
], children: _jsx("p", { children: "Do you want to replace the original file, or save your edits as a new file in this folder?" }) })] }));
|
|
101
148
|
};
|
|
102
149
|
export default ImageEditorComponent;
|
|
@@ -4,6 +4,7 @@ import ImageOptions from "./ImageOptions";
|
|
|
4
4
|
import { useState } from "react";
|
|
5
5
|
import { SAMPLE_IMAGE_URL } from "../../utilities/constants/imageUrls";
|
|
6
6
|
import { useDamConfig } from "../../hocs/DamConfigContext";
|
|
7
|
+
import { withVersion } from "../../utilities/helpers/cacheBust";
|
|
7
8
|
const ImageViewer = ({ file, showFilePreview }) => {
|
|
8
9
|
const damConfig = useDamConfig();
|
|
9
10
|
const { isAdmin } = damConfig;
|
|
@@ -11,6 +12,6 @@ const ImageViewer = ({ file, showFilePreview }) => {
|
|
|
11
12
|
const zoomOptions = ["10", "25", "50", "75", "100"];
|
|
12
13
|
const imgWidth = 750 * (parseInt(zoom) / 100);
|
|
13
14
|
// const imgHeight = 333 * (parseInt(zoom) / 100);
|
|
14
|
-
return (_jsxs("div", { className: "md-lib-relative md-lib-flex md-lib-flex-col md-lib-h-[calc(100vh-65px)] md-lib-w-full md-lib-overflow-hidden", children: [_jsx("div", { className: "md-lib-flex-1 md-lib-flex md-lib-justify-center md-lib-items-center md-lib-w-full md-lib-overflow-auto md-lib-min-h-0", children: _jsx("img", { src: get(file, "s3Url") || get(file, "downloadUrl") || get(file, "thumbnailUrl", SAMPLE_IMAGE_URL), alt: get(file, "name", ""), width: imgWidth, height: "auto", className: "md-lib-max-w-full md-lib-max-h-full md-lib-object-contain" }) }), showFilePreview && (_jsx("div", { className: "md-lib-flex md-lib-justify-center md-lib-items-center md-lib-py-4 md-lib-shrink-0", children: _jsx(ImageOptions, { zoom: zoom, setZoom: setZoom, zoomOptions: zoomOptions, file: file }) }))] }));
|
|
15
|
+
return (_jsxs("div", { className: "md-lib-relative md-lib-flex md-lib-flex-col md-lib-h-[calc(100vh-65px)] md-lib-w-full md-lib-overflow-hidden", children: [_jsx("div", { className: "md-lib-flex-1 md-lib-flex md-lib-justify-center md-lib-items-center md-lib-w-full md-lib-overflow-auto md-lib-min-h-0", children: _jsx("img", { src: withVersion(get(file, "s3Url") || get(file, "downloadUrl") || get(file, "thumbnailUrl", SAMPLE_IMAGE_URL), get(file, "updatedAt")), alt: get(file, "name", ""), width: imgWidth, height: "auto", className: "md-lib-max-w-full md-lib-max-h-full md-lib-object-contain" }) }), showFilePreview && (_jsx("div", { className: "md-lib-flex md-lib-justify-center md-lib-items-center md-lib-py-4 md-lib-shrink-0", children: _jsx(ImageOptions, { zoom: zoom, setZoom: setZoom, zoomOptions: zoomOptions, file: file }) }))] }));
|
|
15
16
|
};
|
|
16
17
|
export default ImageViewer;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Append a version query param so the browser refetches when a file is
|
|
3
|
+
* edited in place (same S3 key, same URL — without this the cached binary
|
|
4
|
+
* is shown after Save). Derived from the file's updatedAt timestamp so the
|
|
5
|
+
* URL only changes when the asset actually changes.
|
|
6
|
+
*/
|
|
7
|
+
export declare const withVersion: (url: string | undefined | null, updatedAt: string | number | Date | null | undefined) => string;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Append a version query param so the browser refetches when a file is
|
|
3
|
+
* edited in place (same S3 key, same URL — without this the cached binary
|
|
4
|
+
* is shown after Save). Derived from the file's updatedAt timestamp so the
|
|
5
|
+
* URL only changes when the asset actually changes.
|
|
6
|
+
*/
|
|
7
|
+
export const withVersion = (url, updatedAt) => {
|
|
8
|
+
if (!url)
|
|
9
|
+
return "";
|
|
10
|
+
if (!updatedAt)
|
|
11
|
+
return url;
|
|
12
|
+
const v = updatedAt instanceof Date
|
|
13
|
+
? updatedAt.getTime()
|
|
14
|
+
: typeof updatedAt === "number"
|
|
15
|
+
? updatedAt
|
|
16
|
+
: new Date(updatedAt).getTime();
|
|
17
|
+
if (!v || Number.isNaN(v))
|
|
18
|
+
return url;
|
|
19
|
+
return url.includes("?") ? `${url}&v=${v}` : `${url}?v=${v}`;
|
|
20
|
+
};
|