Skip to content

Commit

Permalink
[Media] File replacement (#2849)
Browse files Browse the repository at this point in the history
Allows users to replace an existing file with a new file of the same
file type without changing the file's name and url in our DAM.
Resolves #2661 

### Dependencies
- Backend Change:
zesty-io/media-manager-service#150
- New icon: zesty-io/material#105

### Demo


https://github.com/user-attachments/assets/5e68c830-e512-42b9-b6e1-15f6405d5671
  • Loading branch information
finnar-bin authored Aug 1, 2024
1 parent 379c957 commit 21fc276
Show file tree
Hide file tree
Showing 16 changed files with 827 additions and 245 deletions.
15 changes: 7 additions & 8 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@
"@tinymce/tinymce-react": "^4.3.0",
"@welldone-software/why-did-you-render": "^6.1.1",
"@zesty-io/core": "1.10.0",
"@zesty-io/material": "^0.15.2",
"@zesty-io/material": "^0.15.3",
"chart.js": "^3.8.0",
"chartjs-adapter-moment": "^1.0.1",
"chartjs-plugin-datalabels": "^2.0.0",
Expand Down
27 changes: 25 additions & 2 deletions src/apps/content-editor/src/app/components/FieldTypeMedia.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import {
} from "@mui/icons-material";
import { alpha } from "@mui/material/styles";
import { CompactView, Modal, Login } from "@bynder/compact-view";
import { Bynder } from "@zesty-io/material";
import { Bynder, FileReplace } from "@zesty-io/material";

import {
useGetBinsQuery,
Expand All @@ -43,6 +43,8 @@ import styles from "../../../../media/src/app/components/Thumbnail/Loading.less"
import cx from "classnames";
import { FileTypePreview } from "../../../../media/src/app/components/FileModal/FileTypePreview";
import { useGetInstanceSettingsQuery } from "../../../../../shell/services/instance";
import { ReplaceFileModal } from "../../../../media/src/app/components/FileModal/ReplaceFileModal";
import { showReportDialog } from "@sentry/react";

type FieldTypeMediaProps = {
images: string[];
Expand Down Expand Up @@ -597,6 +599,7 @@ const MediaItem = ({
skip: imageZUID?.substr(0, 4) === "http",
});
const [showRenameFileModal, setShowRenameFileModal] = useState(false);
const [isReplaceFileModalOpen, setIsReplaceFileModalOpen] = useState(false);
const [isCopied, setIsCopied] = useState(false);
const [isCopiedZuid, setIsCopiedZuid] = useState(false);
const [newFilename, setNewFilename] = useState("");
Expand Down Expand Up @@ -757,6 +760,7 @@ const MediaItem = ({
<FileTypePreview
src={isURL ? imageZUID : data?.url}
filename={isURL ? imageZUID : data?.filename}
updatedAt={data?.updated_at}
isMediaThumbnail
/>
)}
Expand Down Expand Up @@ -794,7 +798,7 @@ const MediaItem = ({
)}
<Box display="flex" gap={1} justifyContent="flex-end">
{!isBynderAsset || (isBynderAsset && isBynderSessionValid) ? (
<Tooltip title="Replace File" placement="bottom" enterDelay={800}>
<Tooltip title="Swap File" placement="bottom" enterDelay={800}>
<IconButton
size="small"
onClick={(event: any) => {
Expand Down Expand Up @@ -861,6 +865,18 @@ const MediaItem = ({
<ListItemText>Rename</ListItemText>
</MenuItem>
)}
<MenuItem
onClick={(event) => {
event.stopPropagation();
setAnchorEl(null);
setIsReplaceFileModalOpen(true);
}}
>
<ListItemIcon>
<FileReplace />
</ListItemIcon>
<ListItemText>Replace File</ListItemText>
</MenuItem>
{!isURL && !isBynderAsset && (
<MenuItem
onClick={(event) => {
Expand Down Expand Up @@ -914,6 +930,13 @@ const MediaItem = ({
extension={fileExtension(data.filename)}
/>
)}
{isReplaceFileModalOpen && (
<ReplaceFileModal
originalFile={data}
onClose={() => setIsReplaceFileModalOpen(false)}
onCancel={() => setIsReplaceFileModalOpen(false)}
/>
)}
</>
);
};
130 changes: 94 additions & 36 deletions src/apps/media/src/app/components/FileModal/FileModalContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ import ContentCopyRoundedIcon from "@mui/icons-material/ContentCopyRounded";
import DriveFolderUploadRoundedIcon from "@mui/icons-material/DriveFolderUploadRounded";
import FolderRoundedIcon from "@mui/icons-material/FolderRounded";
import DeleteRoundedIcon from "@mui/icons-material/DeleteRounded";
import { FileReplace } from "@zesty-io/material";
import DeleteFileModal from "./DeleteFileModal";
import { MoveFileDialog } from "./MoveFileDialog";
interface Props {
Expand All @@ -59,8 +60,10 @@ interface Props {
role?: string;
};
createdAt?: string;
updatedAt?: string;
handleCloseModal: () => void;
setShowEdit: (show: boolean) => void;
onOpenReplaceFileModal: () => void;
}

export const FileModalContent: FC<Props> = ({
Expand All @@ -72,8 +75,10 @@ export const FileModalContent: FC<Props> = ({
title,
user,
createdAt,
updatedAt,
handleCloseModal,
setShowEdit,
onOpenReplaceFileModal,
}) => {
const [newTitle, setNewTitle] = useState(title);
const [isCopied, setIsCopied] = useState<boolean>(false);
Expand Down Expand Up @@ -276,33 +281,53 @@ export const FileModalContent: FC<Props> = ({
/>
</Stack>
<Box sx={{ display: "flex", flexDirection: "row" }}>
<IconButton
onClick={(evt) => setShowSettingsDropdown(evt.currentTarget)}
aria-controls={openSettings ? "settingsMenu" : undefined}
aria-haspopup="true"
aria-label="Open settings menu"
aria-expanded={openSettings ? "true" : undefined}
size="small"
>
<MoreHorizRoundedIcon fontSize="small" />
</IconButton>
<IconButton size="small" onClick={() => setShowRenameFileModal(true)}>
<EditIcon fontSize="small" />
</IconButton>
<IconButton
size="small"
aria-label="Trash Button"
onClick={() => setShowDeleteFileModal(true)}
>
<DeleteIcon fontSize="small" />
</IconButton>
<IconButton
size="small"
onClick={() => handleCloseModal()}
aria-label="Close Icon"
>
<CloseIcon fontSize="small" />
</IconButton>
<Tooltip placement="bottom-start" title="More">
<IconButton
onClick={(evt) => setShowSettingsDropdown(evt.currentTarget)}
aria-controls={openSettings ? "settingsMenu" : undefined}
aria-haspopup="true"
aria-label="Open settings menu"
aria-expanded={openSettings ? "true" : undefined}
size="small"
>
<MoreHorizRoundedIcon fontSize="small" />
</IconButton>
</Tooltip>
<Tooltip placement="bottom-start" title="Rename File">
<IconButton
size="small"
onClick={() => setShowRenameFileModal(true)}
>
<EditIcon fontSize="small" />
</IconButton>
</Tooltip>
<Tooltip placement="bottom-start" title="Replace File">
<IconButton
size="small"
aria-label="Replace File Button"
onClick={onOpenReplaceFileModal}
>
<FileReplace fontSize="small" />
</IconButton>
</Tooltip>
<Tooltip placement="bottom-start" title="Delete File">
<IconButton
size="small"
aria-label="Trash Button"
onClick={() => setShowDeleteFileModal(true)}
>
<DeleteIcon fontSize="small" />
</IconButton>
</Tooltip>
<Tooltip placement="bottom-start" title="Close Preview">
<IconButton
size="small"
onClick={() => handleCloseModal()}
aria-label="Close Icon"
>
<CloseIcon fontSize="small" />
</IconButton>
</Tooltip>

{/* Settings Dropdown Menu */}
<Menu
Expand Down Expand Up @@ -470,9 +495,7 @@ export const FileModalContent: FC<Props> = ({
/>
</Box>
<Box sx={{ mt: 3 }}>
<Typography color="text.secondary" sx={{ mt: 1 }} variant="body3">
UPLOADED ON
</Typography>
<Typography variant="body2">Uploaded On</Typography>
<Box sx={{ display: "flex", mt: 1 }}>
<Box
sx={{
Expand All @@ -481,18 +504,18 @@ export const FileModalContent: FC<Props> = ({
alignItems: "center",
}}
>
<CalendarTodayIcon sx={{ color: "#101828", opacity: "0.4" }} />
<CalendarTodayIcon sx={{ color: "action.active" }} />
</Box>
<Box sx={{ pl: 3 }}>
<Box sx={{ pl: 2 }}>
<Typography variant="body2">
{moment(createdAt).format("LL")}
</Typography>
<Typography variant="body2" sx={{ color: "text.secondary" }}>
{moment(createdAt).calendar(null, {
lastDay: "[Yesterday] [at] h:mm A ",
sameDay: "[Today] [at] h:mm A ",
lastWeek: "dddd [at] h:mm A ",
sameElse: "dddd [at] h:mm A ",
lastDay: "[Yesterday][,] h:mm A ",
sameDay: "[Today][,] h:mm A ",
lastWeek: "ddd[,] h:mm A ",
sameElse: "ddd[,] h:mm A ",
})}
{new Date(createdAt)
.toLocaleDateString("en-US", {
Expand All @@ -504,6 +527,41 @@ export const FileModalContent: FC<Props> = ({
</Box>
</Box>
</Box>
{!!updatedAt && (
<Box sx={{ mt: 3 }}>
<Typography variant="body2">Updated On</Typography>
<Box sx={{ display: "flex", mt: 1 }}>
<Box
sx={{
display: "flex",
justifyContent: "center",
alignItems: "center",
}}
>
<CalendarTodayIcon sx={{ color: "action.active" }} />
</Box>
<Box sx={{ pl: 2 }}>
<Typography variant="body2">
{moment(updatedAt).format("LL")}
</Typography>
<Typography variant="body2" sx={{ color: "text.secondary" }}>
{moment(updatedAt).calendar(null, {
lastDay: "[Yesterday][,] h:mm A ",
sameDay: "[Today][,] h:mm A ",
lastWeek: "ddd[,] h:mm A ",
sameElse: "ddd[,] h:mm A ",
})}
{new Date(updatedAt)
.toLocaleDateString("en-US", {
day: "2-digit",
timeZoneName: "short",
})
.slice(4)}
</Typography>
</Box>
</Box>
</Box>
)}
</Box>
</Box>
);
Expand Down
10 changes: 9 additions & 1 deletion src/apps/media/src/app/components/FileModal/FileTypePreview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import ReportGmailerrorredIcon from "@mui/icons-material/ReportGmailerrorred";
interface Props {
src: string;
filename: string;
updatedAt?: string;
imageSettings?: any;
isMediaThumbnail?: boolean;
}
Expand All @@ -41,6 +42,7 @@ export const FileTypePreview: FC<Props> = ({
filename,
imageSettings,
isMediaThumbnail,
updatedAt,
}) => {
const theme = useTheme();
const isLargeScreen = useMediaQuery(theme.breakpoints.up("lg"));
Expand Down Expand Up @@ -86,6 +88,11 @@ export const FileTypePreview: FC<Props> = ({
const defaultImageSettings = {
width: 800,
optimize: "high",
// Prevents browser image cache when a certain file has been already replaced
...(!!updatedAt &&
!isNaN(new Date(updatedAt).getTime()) && {
versionHash: new Date(updatedAt).getTime(),
}),
};

if (isLargeScreen) {
Expand Down Expand Up @@ -377,7 +384,8 @@ export const FileTypePreview: FC<Props> = ({
<CardMedia
component="video"
controls={!isMediaThumbnail}
src={src}
// Prevents the browser cache
src={`${src}?versionHash=${new Date(updatedAt).getTime()}`}
sx={{
backgroundColor: "#000",
display: "flex",
Expand Down
Loading

0 comments on commit 21fc276

Please sign in to comment.