Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
400 changes: 246 additions & 154 deletions src/components/core/form/fields/SolidMediaMultipleField.tsx

Large diffs are not rendered by default.

255 changes: 163 additions & 92 deletions src/components/core/form/fields/SolidMediaSingleField.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,23 @@ import { FileReaderExt } from "../../../../components/common/FileReaderExt";
import getAcceptedFileTypes from "../../../../helpers/getAcceptedFileTypes";
import { downloadMediaFile } from "../../../../helpers/downloadMediaFile";
import { getExtensionComponent } from "../../../../helpers/registry";
import { getAbsoluteMediaUrl, openMediaInNewTab, resolveMediaPreviewUrl } from "../../../../helpers/mediaUrl";
import { getMediaPreviewKind, isLightboxMediaKind } from "../../../../helpers/mediaType";
import { SolidMediaFormFieldWidgetProps } from "../../../../types/solid-core";
import { SolidFieldTooltip } from "../../../../components/common/SolidFieldTooltip";
import { ERROR_MESSAGES } from "../../../../constants/error-messages";
import styles from "./solidFields.module.css";
import { SolidIcon } from "../../../shad-cn-ui";
import { getPersistedMediaId } from "./mediaFieldUtils";

type SingleMediaFileDetail = {
name: string;
type: string;
fileUrl: string;
fileSize: number;
originalUrl?: string;
};

export class SolidMediaSingleField implements ISolidField {

private fieldContext: SolidFieldProps;
Expand Down Expand Up @@ -170,7 +180,7 @@ export const DefaultMediaSingleFormEditWidget = ({ formik, fieldContext, setLigh
const isFieldReadonly = formReadonly || fieldReadonly || readOnlyPermission;

const [isDeleteImageDialogVisible, setDeleteImageDialogVisible] = useState(false);
const [fileDetails, setFileDetails] = useState<{ name: string; type: string, fileUrl: string, fileSize: number } | null>(null);
const [fileDetails, setFileDetails] = useState<SingleMediaFileDetail | null>(null);
const [isReplaceImageDialogVisible, setReplaceImageDialogVisible] = useState(false);
const [newFileToUpload, setNewFileToUpload] = useState<any>(null);
const [fileSizeError, setFileSizeError] = useState<string | null>(null);
Expand Down Expand Up @@ -272,34 +282,71 @@ export const DefaultMediaSingleFormEditWidget = ({ formik, fieldContext, setLigh

useEffect(() => {
const fieldValue = formik?.values[fieldLayoutInfo.attrs.name];
let active = true;
const revokeFns: Array<() => void> = [];

const resolveFileDetails = async () => {
if (fieldValue && typeof fieldValue === "object") {
let fileUrl = "";
let fileName = "Unknown File";
let fileSize = 0;
let originalUrl = "";

if (fieldValue instanceof File) {
fileUrl = URL.createObjectURL(fieldValue);
revokeFns.push(() => URL.revokeObjectURL(fileUrl));
originalUrl = fileUrl;
fileName = fieldValue.name;
fileSize = fieldValue.size;
} else if (fieldValue._full_url) {
const resolved = await resolveMediaPreviewUrl(fieldValue._full_url);
if (resolved.revoke) {
revokeFns.push(resolved.revoke);
}
fileUrl = resolved.url;
originalUrl = getAbsoluteMediaUrl(fieldValue._full_url || fieldValue.relativeUri);
fileName = fieldValue.originalFileName;
fileSize = fieldValue.fileSize;
}

if (fieldValue && typeof fieldValue === "object") {
let fileUrl = "";
let fileName = "Unknown File";
let fileSize = 0;

if (fieldValue instanceof File) {
fileUrl = URL.createObjectURL(fieldValue);
fileName = fieldValue.name;
fileSize = fieldValue.size;
} else if (fieldValue._full_url) {
fileUrl = fieldValue._full_url;
fileName = fieldValue.originalFileName;
fileSize = fieldValue.fileSize;
}
if (!active) {
revokeFns.forEach((fn) => fn());
return;
}

setFileDetails({
name: fileName,
type: fieldValue.mimeType ? fieldValue.mimeType : fieldValue.type,
fileUrl,
fileSize
});
setFileDetails({
name: fileName,
type: fieldValue.mimeType ? fieldValue.mimeType : fieldValue.type,
fileUrl,
fileSize,
originalUrl,
});

formik.setFieldValue(fieldLayoutInfo.attrs.name, fieldValue);
return;
}

// Ensure formik has the correct value
formik.setFieldValue(fieldLayoutInfo.attrs.name, fieldValue);
} else {
setFileDetails(null);
}
};

resolveFileDetails().catch(() => {
if (!active) return;
if (fieldValue && typeof fieldValue === "object" && fieldValue._full_url) {
setFileDetails({
name: fieldValue.originalFileName,
type: fieldValue.mimeType ? fieldValue.mimeType : fieldValue.type,
fileUrl: getAbsoluteMediaUrl(fieldValue._full_url || fieldValue.relativeUri),
fileSize: fieldValue.fileSize,
originalUrl: getAbsoluteMediaUrl(fieldValue._full_url || fieldValue.relativeUri),
});
return;
}
setFileDetails(null);
});
return () => {
active = false;
revokeFns.forEach((fn) => fn());
};
}, [formik.values, fieldLayoutInfo.attrs.name]);

const {
Expand Down Expand Up @@ -332,32 +379,25 @@ export const DefaultMediaSingleFormEditWidget = ({ formik, fieldContext, setLigh
}

const handleFileView = (url: any) => {
const downloadOnlyExt = [
"txt", "zip", "rar",
"doc", "docx",
"xls", "xlsx",
"ppt", "pptx"
];

const fileUrl = url?.fileUrl || "";
const cleanUrl = fileUrl.split("?")[0];
const ext = cleanUrl.split(".").pop()?.toLowerCase();

if (ext && downloadOnlyExt.includes(ext)) {
const link = document.createElement('a');
link.href = url.fileUrl;
link.download = ''; // or specify a file name like 'file.pdf'
link.target = '_blank';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
const previewKind = getMediaPreviewKind({
url: url?.originalUrl || url?.fileUrl,
fileName: url?.name,
mimeType: url?.type,
});

} else {
if (isLightboxMediaKind(previewKind)) {
setLightboxUrls?.([
{ src: url.fileUrl, downloadUrl: url.fileUrl },
{
src: url.fileUrl,
downloadUrl: url.fileUrl,
type: previewKind === "video" ? "video" : undefined
},
]);
setOpenLightbox?.(true);
return;
}

void openMediaInNewTab(url?.originalUrl || url?.fileUrl);
}


Expand Down Expand Up @@ -412,7 +452,7 @@ export const DefaultMediaSingleFormEditWidget = ({ formik, fieldContext, setLigh
className="solid-file-icon-btn"
disabled={isFieldDisabled || isFieldReadonly}
aria-label="Download file"
onClick={() => downloadMediaFile(fileDetails?.fileUrl, fileDetails?.name)}
onClick={() => downloadMediaFile(fileDetails?.originalUrl || fileDetails?.fileUrl, fileDetails?.name)}
>
<SolidIcon name="si-download" aria-hidden />
</button>
Expand Down Expand Up @@ -484,7 +524,7 @@ export const DefaultMediaSingleFormEditWidget = ({ formik, fieldContext, setLigh
}

export const DefaultMediaSingleFormViewWidget = ({ formik, fieldContext, setLightboxUrls, setOpenLightbox }: SolidMediaFormFieldWidgetProps) => {
const [fileDetails, setFileDetails] = useState<{ name: string; type: string, fileUrl: string, fileSize: number } | null>(null);
const [fileDetails, setFileDetails] = useState<SingleMediaFileDetail | null>(null);
const fieldMetadata = fieldContext.fieldMetadata;
const fieldLayoutInfo = fieldContext.field;
const className = fieldLayoutInfo.attrs?.className || 'field w-full px-2 pt-2';
Expand All @@ -502,60 +542,91 @@ export const DefaultMediaSingleFormViewWidget = ({ formik, fieldContext, setLigh

useEffect(() => {
const fieldValue = formik?.values[fieldLayoutInfo.attrs.name];
let active = true;
const revokeFns: Array<() => void> = [];

const resolveFileDetails = async () => {
if (fieldValue && typeof fieldValue === "object") {
let fileUrl = "";
let fileName = "Unknown File";
let fileSize = 0;
let originalUrl = "";

if (fieldValue instanceof File) {
fileUrl = URL.createObjectURL(fieldValue);
revokeFns.push(() => URL.revokeObjectURL(fileUrl));
originalUrl = fileUrl;
fileName = fieldValue.name;
fileSize = fieldValue.size;
} else if (fieldValue._full_url) {
const resolved = await resolveMediaPreviewUrl(fieldValue._full_url);
if (resolved.revoke) {
revokeFns.push(resolved.revoke);
}
fileUrl = resolved.url;
originalUrl = getAbsoluteMediaUrl(fieldValue._full_url || fieldValue.relativeUri);
fileName = fieldValue.originalFileName;
fileSize = fieldValue.fileSize;
}

if (!active) {
revokeFns.forEach((fn) => fn());
return;
}

if (fieldValue && typeof fieldValue === "object") {
let fileUrl = "";
let fileName = "Unknown File";
let fileSize = 0;

if (fieldValue instanceof File) {
fileUrl = URL.createObjectURL(fieldValue);
fileName = fieldValue.name;
fileSize = fieldValue.size;
} else if (fieldValue._full_url) {
fileUrl = fieldValue._full_url;
fileName = fieldValue.originalFileName;
fileSize = fieldValue.fileSize;
setFileDetails({
name: fileName,
type: fieldValue.mimeType ? fieldValue.mimeType : fieldValue.type,
fileUrl,
fileSize,
originalUrl,
});
return;
}

setFileDetails({
name: fileName,
type: fieldValue.mimeType ? fieldValue.mimeType : fieldValue.type,
fileUrl,
fileSize
});
// Ensure formik has the correct value
formik.setFieldValue(fieldLayoutInfo.attrs.name, fieldValue);
}
setFileDetails(null);
};

resolveFileDetails().catch(() => {
if (!active) return;
if (fieldValue && typeof fieldValue === "object" && fieldValue._full_url) {
setFileDetails({
name: fieldValue.originalFileName,
type: fieldValue.mimeType ? fieldValue.mimeType : fieldValue.type,
fileUrl: getAbsoluteMediaUrl(fieldValue._full_url || fieldValue.relativeUri),
fileSize: fieldValue.fileSize,
originalUrl: getAbsoluteMediaUrl(fieldValue._full_url || fieldValue.relativeUri),
});
return;
}
setFileDetails(null);
});
return () => {
active = false;
revokeFns.forEach((fn) => fn());
};
}, [formik.values, fieldLayoutInfo.attrs.name]);

const handleFileView = (url: any) => {
const downloadOnlyExt = [
"txt", "zip", "rar",
"doc", "docx",
"xls", "xlsx",
"ppt", "pptx"
];

const fileUrl = url?.fileUrl || "";
const cleanUrl = fileUrl.split("?")[0];
const ext = cleanUrl.split(".").pop()?.toLowerCase();

if (ext && downloadOnlyExt.includes(ext)) {
const link = document.createElement('a');
link.href = url.fileUrl;
link.download = ''; // or specify a file name like 'file.pdf'
link.target = '_blank';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
const previewKind = getMediaPreviewKind({
url: url?.originalUrl || url?.fileUrl,
fileName: url?.name,
mimeType: url?.type,
});

} else {
if (isLightboxMediaKind(previewKind)) {
setLightboxUrls?.([
{ src: url.fileUrl, downloadUrl: url.fileUrl },
{
src: url.fileUrl,
downloadUrl: url.fileUrl,
type: previewKind === "video" ? "video" : undefined
},
]);
setOpenLightbox?.(true);
return;
}

void openMediaInNewTab(url?.originalUrl || url?.fileUrl);
}

return (
Expand Down Expand Up @@ -585,7 +656,7 @@ export const DefaultMediaSingleFormViewWidget = ({ formik, fieldContext, setLigh
type="button"
className="solid-file-icon-btn"
aria-label="Download file"
onClick={() => downloadMediaFile(fileDetails?.fileUrl, fileDetails?.name)}
onClick={() => downloadMediaFile(fileDetails?.originalUrl || fileDetails?.fileUrl, fileDetails?.name)}
>
<SolidIcon name="si-download" aria-hidden />
</button>
Expand Down
Loading