@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("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(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 uploadUrl = get(presigned, ["data", 0, "url"]);
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("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(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;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bindu-dashing/dam-solution-v2",
3
- "version": "5.9.256",
3
+ "version": "5.9.259",
4
4
  "dependencies": {
5
5
  "@ant-design/icons": "^5.0.1",
6
6
  "@emoji-mart/data": "^1.2.1",