@bindu-dashing/dam-solution-v2 5.9.256 → 5.9.259
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 RefreshableImage from "../common/RefreshableImage";
|
|
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(
|
|
55
|
+
_jsx(Image, { src: UPLOAD_FILE_IN_PROGRESS_WHITE_IMAGE, width: 100, height: 100 })) : (_jsx(RefreshableImage, { src: get(file, "thumbnailUrl", ""), version: 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;
|
|
@@ -19,7 +19,7 @@ import { get } from "lodash";
|
|
|
19
19
|
import CustomButton from "../../common/Button";
|
|
20
20
|
import { showNotification } from "../../common/notifications";
|
|
21
21
|
import { FILE_UPLOAD_ERROR, SOMETHING_WENT_WRONG, UPDATE_SUCCESS, } from "../../utilities/constants/messages";
|
|
22
|
-
import { FILE_UPLOAD_URL, SAVE_EDITED_FILE_THUMBNAIL_URL, SAVE_EDITED_FILE_URL, } from "../../utilities/constants/apiUrls";
|
|
22
|
+
import { CREATE_FILE_URL, FILE_UPLOAD_URL, SAVE_EDITED_FILE_THUMBNAIL_URL, SAVE_EDITED_FILE_URL, } from "../../utilities/constants/apiUrls";
|
|
23
23
|
import { generateFoldersQueryKey, invalidateData, QueryKeys, } from "../../utilities/constants/queryKeys";
|
|
24
24
|
import { useQueryClient } from "react-query";
|
|
25
25
|
import { useDamConfig } from "../../hocs/DamConfigContext";
|
|
@@ -50,6 +50,10 @@ const ImageEditorComponent = ({ file, handleClose, }) => {
|
|
|
50
50
|
const invalidateAfterChange = () => {
|
|
51
51
|
invalidateData(queryClient, generateFoldersQueryKey(type), currentFolderId);
|
|
52
52
|
invalidateData(queryClient, QueryKeys.FILE, get(file, "_id"));
|
|
53
|
+
// Image picker uses ["image-pickers", id] as its own query key,
|
|
54
|
+
// so the above invalidations don't refresh the picker grid. Hit it
|
|
55
|
+
// by prefix so we don't need to know the specific picker id here.
|
|
56
|
+
queryClient.invalidateQueries({ queryKey: [QueryKeys.IMAGE_PICKERS] });
|
|
53
57
|
};
|
|
54
58
|
const handleOverwrite = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
55
59
|
var _a;
|
|
@@ -106,30 +110,55 @@ const ImageEditorComponent = ({ file, handleClose, }) => {
|
|
|
106
110
|
const newName = buildEditedName(get(file, "name", "edited.png"));
|
|
107
111
|
const mimetype = editedFile.type || get(file, "mimetype") || "image/png";
|
|
108
112
|
try {
|
|
113
|
+
// 1) ask backend for a presigned S3 upload URL
|
|
109
114
|
const presigned = yield api.post(FILE_UPLOAD_URL, {
|
|
110
115
|
brandId,
|
|
111
116
|
folderId: currentFolderId,
|
|
112
117
|
files: [{ name: newName, size: editedFile.size }],
|
|
113
118
|
});
|
|
114
|
-
const
|
|
119
|
+
const uploadEntry = get(presigned, ["data", 0]);
|
|
120
|
+
const uploadUrl = get(uploadEntry, "url");
|
|
121
|
+
const filePath = get(uploadEntry, "filePath");
|
|
115
122
|
if (!uploadUrl) {
|
|
116
123
|
showNotification(FILE_UPLOAD_ERROR, NotificationStatus.ERROR);
|
|
117
124
|
setState((prev) => (Object.assign(Object.assign({}, prev), { loading: false })));
|
|
118
125
|
return;
|
|
119
126
|
}
|
|
127
|
+
// 2) PUT the edited binary to S3
|
|
120
128
|
const s3Resp = yield fetch(uploadUrl, {
|
|
121
129
|
method: "PUT",
|
|
122
130
|
headers: { "Content-Type": mimetype },
|
|
123
131
|
body: editedFile,
|
|
124
132
|
});
|
|
125
|
-
if (s3Resp.ok) {
|
|
126
|
-
invalidateAfterChange();
|
|
127
|
-
showNotification(UPDATE_SUCCESS, NotificationStatus.SUCCESS);
|
|
128
|
-
handleClose();
|
|
129
|
-
}
|
|
130
|
-
else {
|
|
133
|
+
if (!s3Resp.ok) {
|
|
131
134
|
showNotification(FILE_UPLOAD_ERROR, NotificationStatus.ERROR);
|
|
135
|
+
setState((prev) => (Object.assign(Object.assign({}, prev), { loading: false })));
|
|
136
|
+
return;
|
|
132
137
|
}
|
|
138
|
+
// 3) register the file in the DB so it shows up in the folder grid.
|
|
139
|
+
// Inherit assetId / metadata / teamIds from the original file so the new
|
|
140
|
+
// one belongs to the same asset template and keeps the same field values.
|
|
141
|
+
const inheritedMetadata = get(file, "metadata") || {};
|
|
142
|
+
const inheritedTeamIds = get(file, "teamIds") || [];
|
|
143
|
+
yield api.post(CREATE_FILE_URL, {
|
|
144
|
+
assetId: get(file, "assetId"),
|
|
145
|
+
teamIds: inheritedTeamIds,
|
|
146
|
+
metadata: inheritedMetadata,
|
|
147
|
+
folderId: currentFolderId,
|
|
148
|
+
files: [
|
|
149
|
+
{
|
|
150
|
+
name: newName,
|
|
151
|
+
path: filePath,
|
|
152
|
+
size: editedFile.size,
|
|
153
|
+
mimetype,
|
|
154
|
+
metadata: inheritedMetadata,
|
|
155
|
+
teamIds: inheritedTeamIds,
|
|
156
|
+
},
|
|
157
|
+
],
|
|
158
|
+
});
|
|
159
|
+
invalidateAfterChange();
|
|
160
|
+
showNotification(UPDATE_SUCCESS, NotificationStatus.SUCCESS);
|
|
161
|
+
handleClose();
|
|
133
162
|
}
|
|
134
163
|
catch (error) {
|
|
135
164
|
showNotification(get(error, "message", SOMETHING_WENT_WRONG), NotificationStatus.ERROR);
|
|
@@ -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 RefreshableImage from "../../common/RefreshableImage";
|
|
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(
|
|
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(RefreshableImage, { src: get(file, "s3Url") || get(file, "downloadUrl") || get(file, "thumbnailUrl", SAMPLE_IMAGE_URL), version: 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,17 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
/**
|
|
3
|
+
* <img> wrapper that bypasses the browser HTTP cache when `version` changes.
|
|
4
|
+
*
|
|
5
|
+
* Why: S3 presigned URLs don't change after a file is overwritten in place
|
|
6
|
+
* (same key → same signed URL). The browser's HTTP cache then serves the
|
|
7
|
+
* old binary even when react-query refetches the file list. Appending
|
|
8
|
+
* query params would break the S3 signature, so instead we re-fetch the URL
|
|
9
|
+
* with `cache: 'reload'` and display the result via an object URL — the
|
|
10
|
+
* blob URL is unique so the browser must paint the fresh bytes.
|
|
11
|
+
*/
|
|
12
|
+
type Props = Omit<React.ImgHTMLAttributes<HTMLImageElement>, "src"> & {
|
|
13
|
+
src: string;
|
|
14
|
+
version?: string | number | null;
|
|
15
|
+
};
|
|
16
|
+
declare const RefreshableImage: React.FC<Props>;
|
|
17
|
+
export default RefreshableImage;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
var __rest = (this && this.__rest) || function (s, e) {
|
|
2
|
+
var t = {};
|
|
3
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
4
|
+
t[p] = s[p];
|
|
5
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
6
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
7
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
8
|
+
t[p[i]] = s[p[i]];
|
|
9
|
+
}
|
|
10
|
+
return t;
|
|
11
|
+
};
|
|
12
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
13
|
+
import { useEffect, useRef, useState } from "react";
|
|
14
|
+
const RefreshableImage = (_a) => {
|
|
15
|
+
var { src, version } = _a, imgProps = __rest(_a, ["src", "version"]);
|
|
16
|
+
const [blobSrc, setBlobSrc] = useState(null);
|
|
17
|
+
const initialVersion = useRef(version);
|
|
18
|
+
useEffect(() => {
|
|
19
|
+
if (!version || version === initialVersion.current)
|
|
20
|
+
return;
|
|
21
|
+
let canceled = false;
|
|
22
|
+
fetch(src, { cache: "reload" })
|
|
23
|
+
.then((r) => r.blob())
|
|
24
|
+
.then((blob) => {
|
|
25
|
+
if (canceled)
|
|
26
|
+
return;
|
|
27
|
+
setBlobSrc((prev) => {
|
|
28
|
+
if (prev)
|
|
29
|
+
URL.revokeObjectURL(prev);
|
|
30
|
+
return URL.createObjectURL(blob);
|
|
31
|
+
});
|
|
32
|
+
})
|
|
33
|
+
.catch(() => {
|
|
34
|
+
/* fall through to original src */
|
|
35
|
+
});
|
|
36
|
+
return () => {
|
|
37
|
+
canceled = true;
|
|
38
|
+
};
|
|
39
|
+
}, [version, src]);
|
|
40
|
+
useEffect(() => {
|
|
41
|
+
return () => {
|
|
42
|
+
if (blobSrc)
|
|
43
|
+
URL.revokeObjectURL(blobSrc);
|
|
44
|
+
};
|
|
45
|
+
}, [blobSrc]);
|
|
46
|
+
return _jsx("img", Object.assign({ src: blobSrc !== null && blobSrc !== void 0 ? blobSrc : src }, imgProps));
|
|
47
|
+
};
|
|
48
|
+
export default RefreshableImage;
|