Show friendly message for non-previewable file previews (#16372)
<img width="1507" height="807" alt="Screenshot 2025-12-06 at 12 50 42 AM" src="https://github.com/user-attachments/assets/9e13d813-76fa-43e9-9dea-ead1dca4ca1f" /> Closes #15771
This commit is contained in:
@@ -15,7 +15,6 @@ import styled from '@emotion/styled';
|
||||
import { useState } from 'react';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { PREVIEWABLE_EXTENSIONS } from '@/activities/files/const/previewable-extensions.const';
|
||||
import { FileIcon } from '@/file/components/FileIcon';
|
||||
import { useHasPermissionFlag } from '@/settings/roles/hooks/useHasPermissionFlag';
|
||||
import { IconCalendar, OverflowingTextWithTooltip } from 'twenty-ui/display';
|
||||
@@ -90,10 +89,6 @@ export const AttachmentRow = ({
|
||||
const { name: originalFileName, extension: attachmentFileExtension } =
|
||||
getFileNameAndExtension(attachment.name);
|
||||
|
||||
const fileExtension =
|
||||
attachmentFileExtension?.toLowerCase().replace('.', '') ?? '';
|
||||
const isPreviewable = PREVIEWABLE_EXTENSIONS.includes(fileExtension);
|
||||
|
||||
const [attachmentFileName, setAttachmentFileName] =
|
||||
useState(originalFileName);
|
||||
|
||||
@@ -180,22 +175,14 @@ export const AttachmentRow = ({
|
||||
/>
|
||||
) : (
|
||||
<StyledLinkContainer>
|
||||
{isPreviewable ? (
|
||||
<StyledLink
|
||||
onClick={handleOpenDocument}
|
||||
href={attachment.fullPath}
|
||||
>
|
||||
<OverflowingTextWithTooltip text={attachment.name} />
|
||||
</StyledLink>
|
||||
) : (
|
||||
<StyledLink
|
||||
href={attachment.fullPath}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<OverflowingTextWithTooltip text={attachment.name} />
|
||||
</StyledLink>
|
||||
)}
|
||||
<StyledLink
|
||||
onClick={handleOpenDocument}
|
||||
href={attachment.fullPath}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<OverflowingTextWithTooltip text={attachment.name} />
|
||||
</StyledLink>
|
||||
</StyledLinkContainer>
|
||||
)}
|
||||
</StyledLeftContent>
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { PREVIEWABLE_EXTENSIONS } from '@/activities/files/const/previewable-extensions.const';
|
||||
import { downloadFile } from '@/activities/files/utils/downloadFile';
|
||||
import { fetchCsvPreview } from '@/activities/files/utils/fetchCsvPreview';
|
||||
import { getFileType } from '@/activities/files/utils/getFileType';
|
||||
import DocViewer, { DocViewerRenderers } from '@cyntler/react-doc-viewer';
|
||||
import '@cyntler/react-doc-viewer/dist/index.css';
|
||||
import { useTheme } from '@emotion/react';
|
||||
@@ -7,6 +9,8 @@ import styled from '@emotion/styled';
|
||||
import { Trans } from '@lingui/react/macro';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { IconDownload } from 'twenty-ui/display';
|
||||
import { Button } from 'twenty-ui/input';
|
||||
import { getFileNameAndExtension } from '~/utils/file/getFileNameAndExtension';
|
||||
|
||||
const StyledDocumentViewerContainer = styled.div`
|
||||
@@ -37,6 +41,29 @@ const StyledDocumentViewerContainer = styled.div`
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledUnavailablePreviewContainer = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
gap: ${({ theme }) => theme.spacing(4)};
|
||||
padding: ${({ theme }) => theme.spacing(8)};
|
||||
text-align: center;
|
||||
`;
|
||||
|
||||
const StyledMessage = styled.div`
|
||||
color: ${({ theme }) => theme.font.color.secondary};
|
||||
font-size: ${({ theme }) => theme.font.size.lg};
|
||||
max-width: 400px;
|
||||
`;
|
||||
|
||||
const StyledTitle = styled.div`
|
||||
color: ${({ theme }) => theme.font.color.primary};
|
||||
font-size: ${({ theme }) => theme.font.size.xl};
|
||||
font-weight: ${({ theme }) => theme.font.weight.semiBold};
|
||||
`;
|
||||
|
||||
type DocumentViewerProps = {
|
||||
documentName: string;
|
||||
documentUrl: string;
|
||||
@@ -77,6 +104,9 @@ export const DocumentViewer = ({
|
||||
|
||||
const { extension } = getFileNameAndExtension(documentName);
|
||||
const fileExtension = extension?.toLowerCase().replace('.', '') ?? '';
|
||||
const fileCategory = getFileType(documentName);
|
||||
const isPreviewable = PREVIEWABLE_EXTENSIONS.includes(fileExtension);
|
||||
|
||||
const mimeType = PREVIEWABLE_EXTENSIONS.includes(fileExtension)
|
||||
? MIME_TYPE_MAPPING[fileExtension]
|
||||
: undefined;
|
||||
@@ -89,6 +119,37 @@ export const DocumentViewer = ({
|
||||
}
|
||||
}, [documentUrl, fileExtension]);
|
||||
|
||||
if (!isPreviewable) {
|
||||
return (
|
||||
<StyledDocumentViewerContainer>
|
||||
<StyledUnavailablePreviewContainer>
|
||||
<StyledTitle>
|
||||
<Trans>Preview Not Available</Trans>
|
||||
</StyledTitle>
|
||||
<StyledMessage>
|
||||
{fileCategory === 'ARCHIVE' ? (
|
||||
<Trans>
|
||||
Archive files cannot be previewed. Please download the file to
|
||||
access its contents.
|
||||
</Trans>
|
||||
) : (
|
||||
<Trans>
|
||||
This file type cannot be previewed. Please download the file to
|
||||
view it.
|
||||
</Trans>
|
||||
)}
|
||||
</StyledMessage>
|
||||
<Button
|
||||
Icon={IconDownload}
|
||||
title="Download File"
|
||||
onClick={() => downloadFile(documentUrl, documentName)}
|
||||
variant="secondary"
|
||||
/>
|
||||
</StyledUnavailablePreviewContainer>
|
||||
</StyledDocumentViewerContainer>
|
||||
);
|
||||
}
|
||||
|
||||
if (fileExtension === 'csv' && !isDefined(csvPreview))
|
||||
return (
|
||||
<StyledDocumentViewerContainer>
|
||||
|
||||
Reference in New Issue
Block a user