Skip to content

Commit

Permalink
Merge branch 'development' into fix/folder-label
Browse files Browse the repository at this point in the history
  • Loading branch information
tomicvladan authored Sep 21, 2023
2 parents 56f77ef + afdcfb2 commit 9450c44
Show file tree
Hide file tree
Showing 6 changed files with 137 additions and 55 deletions.
133 changes: 113 additions & 20 deletions src/components/FilePreview/FilePreview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Button } from '@components/Buttons';
import ConsentViewer from '@components/ConsentViewer/ConsentViewer';
import { useLocales } from '@context/LocalesContext';
import { FileItem } from '@fairdatasociety/fdp-storage';
import { FC } from 'react';
import { FC, useEffect, useState } from 'react';

interface FilePreviewProps {
file: FileItem;
Expand All @@ -13,6 +13,28 @@ interface FilePreviewProps {
onError: () => void;
}

const MAX_TEXT_PREVIEW_LENGTH = 500;

enum PreviewType {
Consent,
Text,
Video,
Image,
Unknown,
}

const TEXT_FILE_EXTENSIONS = [
'.txt',
'.srt',
'.log',
'.csv',
'.sub',
'.md',
'.json',
];

const IMAGE_FILE_EXTENSIONS = ['.jpg', '.jpeg', '.png', '.svg', '.gif', '.bmp'];

const VIDEO_FILE_EXTENSIONS = [
'.mpg',
'.mp2',
Expand All @@ -32,10 +54,31 @@ const VIDEO_FILE_EXTENSIONS = [
'.swf',
];

export function isFilePreviewSupported(fileName: string): boolean {
return isTextFile(fileName) || isFileImage(fileName) || isFileVideo(fileName);
}

function isString(data: unknown): boolean {
return typeof data === 'string';
}

function checkFileExtension(fileName: string, extensions: string[]): boolean {
const lowercaseFileName = fileName.toLowerCase();
return extensions.some((extension) => lowercaseFileName.endsWith(extension));
}

function isTextFile(fileName: string): boolean {
return checkFileExtension(fileName, TEXT_FILE_EXTENSIONS);
}

function isFileImage(fileName: string): boolean {
return checkFileExtension(fileName, IMAGE_FILE_EXTENSIONS);
}

function isFileVideo(fileName: string): boolean {
return checkFileExtension(fileName, VIDEO_FILE_EXTENSIONS);
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
function isFileConsent(data: any): boolean {
return (
Expand All @@ -54,13 +97,6 @@ function isFileConsent(data: any): boolean {
);
}

function isFileVideo(fileName: string): boolean {
const lowercaseFileName = fileName.toLowerCase();
return VIDEO_FILE_EXTENSIONS.some((extension) =>
lowercaseFileName.endsWith(extension)
);
}

const FilePreview: FC<FilePreviewProps> = ({
file,
pod,
Expand All @@ -69,12 +105,59 @@ const FilePreview: FC<FilePreviewProps> = ({
onError,
}) => {
const { intl } = useLocales();
const [type, setType] = useState<PreviewType | null>(null);
const [content, setContent] = useState<unknown>(source);

const preparePreview = async () => {
try {
const fileName = file.name;

if (isFileVideo(fileName)) {
setContent(window.URL.createObjectURL(source));
return setType(PreviewType.Video);
}

if (isFileImage(fileName)) {
setContent(window.URL.createObjectURL(source));
return setType(PreviewType.Image);
}

if (isTextFile(fileName)) {
const text = await source.text();

if (fileName.endsWith('.json')) {
const json = JSON.parse(text);

if (isFileConsent(json)) {
setType(PreviewType.Consent);
return setContent(json);
}
}

setContent(
text.substring(0, MAX_TEXT_PREVIEW_LENGTH) +
(text.length > MAX_TEXT_PREVIEW_LENGTH ? '...' : '')
);
return setType(PreviewType.Text);
}

throw new Error('Unknown type');
} catch (error) {
setType(PreviewType.Unknown);

onError();
}
};

useEffect(() => {
preparePreview();
}, [file, source]);

if (isFileConsent(source)) {
if (type === PreviewType.Consent) {
return (
<>
<div className="w-full h-auto my-10 rounded">
<ConsentViewer data={source} />
<ConsentViewer data={content} />
</div>
{(!directory || !directory.includes('/')) && (
<div className="mb-4">
Expand All @@ -97,22 +180,32 @@ const FilePreview: FC<FilePreviewProps> = ({
);
}

if (isFileVideo(file.name || '')) {
if (type === PreviewType.Text) {
return (
<p className="w-full h-auto my-10 max-h-48 overflow-auto">{content}</p>
);
}

if (type === PreviewType.Video) {
return (
<video className="w-full h-auto my-10 rounded" controls>
<source src={source} />
<source src={content as string} />
</video>
);
}

return (
<img
src={source}
alt="Preview Image"
className="w-full h-auto my-10 rounded"
onError={onError}
/>
);
if (type === PreviewType.Image) {
return (
<img
src={content as string}
alt="Preview Image"
className="w-full h-auto my-10 rounded"
onError={onError}
/>
);
}

return null;
};

export default FilePreview;
2 changes: 1 addition & 1 deletion src/components/Forms/LoginForm/LoginForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ const LoginForm: FC = () => {
disabled={!isValid}
type="submit"
variant="secondary"
label={intl.get('CONTINUE')}
label={intl.get('LOGIN')}
/>
</div>

Expand Down
25 changes: 13 additions & 12 deletions src/components/Modals/PreviewFileModal/PreviewFileModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@ import ShareDarkIcon from '@media/UI/share-dark.svg';
import DeleteLightIcon from '@media/UI/delete-light.svg';
import DeleteDarkIcon from '@media/UI/delete-dark.svg';
import Spinner from '@components/Spinner/Spinner';
import FilePreview from '@components/FilePreview/FilePreview';
import FilePreview, {
isFilePreviewSupported,
} from '@components/FilePreview/FilePreview';
import { FileItem } from '@fairdatasociety/fdp-storage';
import { extractFileExtension } from '@utils/filename';
import { useLocales } from '@context/LocalesContext';
Expand All @@ -54,13 +56,17 @@ const PreviewFileModal: FC<PreviewModalProps> = ({
const { activePod, directoryName } = useContext(PodContext);
const [loading, setLoading] = useState(false);

const [imageSource, setImageSource] = useState('');
const [fileContent, setFileContent] = useState<Blob | null>(null);
const [errorMessage, setErrorMessage] = useState('');
const [showShareFileModal, setShowShareFileModal] = useState(false);
const [showConfirmDeleteModal, setShowConfirmDeleteModal] = useState(false);
const { intl } = useLocales();

useEffect(() => {
if (!isFilePreviewSupported(previewFile?.name)) {
return;
}

setLoading(true);
downloadFile(fdpClientRef.current, {
filename: previewFile?.name,
Expand All @@ -71,12 +77,7 @@ const PreviewFileModal: FC<PreviewModalProps> = ({
const blob = await response.arrayBuffer();
const content = new Blob([blob]);

if (previewFile?.name.endsWith('.json')) {
const json = await content.text();
return setImageSource(JSON.parse(json));
}

setImageSource(window.URL.createObjectURL(content));
setFileContent(content);
})
.catch((e) => {
setErrorMessage(intl.get('FILE_PREVIEW_ERROR'));
Expand Down Expand Up @@ -152,24 +153,24 @@ const PreviewFileModal: FC<PreviewModalProps> = ({
headerTitle={intl.get('PREVIEW_FILE')}
className="w-full md:w-98"
>
{imageSource ? (
{fileContent ? (
<FilePreview
file={previewFile}
source={imageSource}
source={fileContent}
pod={activePod}
directory={directoryName}
onError={() => setErrorMessage(intl.get('FILE_PREVIEW_ERROR'))}
/>
) : null}
<Spinner isLoading={loading} />
<Spinner className="my-8" isLoading={loading} />

{errorMessage ? (
<div className="my-28 text-color-status-negative-day text-xs text-center leading-none">
{errorMessage}
</div>
) : null}

<h2 className="text-base text-color-accents-purple-black dark:text-color-shade-white-night">
<h2 className="text-base mt-8 text-color-accents-purple-black dark:text-color-shade-white-night">
{previewFile?.name}
</h2>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,24 +20,6 @@ const AuthenticationNavbar: FC = () => {
<Logo />

<div className="flex justify-between items-center">
<Link href="/import">
<a className={`${classes.button} mr-4`}>
<span className="hidden md:inline">
<Button
variant="tertiary-outlined"
label={intl.get('IMPORT_ACCOUNT')}
icon={<DownloadIcon className="inline-block ml-2" />}
/>
</span>
<span className="md:hidden">
<Button
variant="tertiary-outlined"
icon={<DownloadIcon className="inline-block" />}
/>
</span>
</a>
</Link>

<a className={`${classes.button} mr-4`}>
<ConnectDropdown />
</a>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ const DriveActionBar = ({
type="button"
variant="primary"
className="mx-1 p-0"
disabled={!activePod}
onClick={onFileUploadClick}
>
<span className="mr-2">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import CreateFolderLightIcon from '@media/UI/create-folder-light.svg';
import CreateFolderDarkIcon from '@media/UI/create-folder-dark.svg';
import { UpdateDriveProps } from '@interfaces/handlers';
import { useLocales } from '@context/LocalesContext';
import PodContext from '@context/PodContext';

export interface DriveActionBarMobileProps extends UpdateDriveProps {
refreshPods?: () => void;
Expand All @@ -32,12 +33,13 @@ const DriveActionBarItem = (
label: string,
imageLight: JSX.Element,
imageDark: JSX.Element,
onClick: () => void
onClick: () => void,
disabled = false
) => {
return (
<div
className="py-2 w-24 block md:hidden sm:w-full py-1 flex-shrink-0 md:py-4 shadow cursor-pointer hover:bg-color-shade-dark-4-day dark:hover:bg-color-shade-dark-2-night"
onClick={onClick}
onClick={disabled ? undefined : onClick}
>
<a className="flex flex-col justify-center items-center">
{theme === 'light' ? imageLight : imageDark}
Expand All @@ -55,6 +57,7 @@ const DriveActionBarMobile: FC<DriveActionBarMobileProps> = ({
refreshPods,
}) => {
const { theme } = useContext(ThemeContext);
const { activePod } = useContext(PodContext);

const [showCreatePodModal, setShowCreatePodModal] = useState(false);
const [showUploadFileModal, setShowUploadFileModal] = useState(false);
Expand All @@ -77,7 +80,8 @@ const DriveActionBarMobile: FC<DriveActionBarMobileProps> = ({
intl.get('UPLOAD'),
<UploadLightIcon height="22" />,
<UploadDarkIcon height="22" />,
() => setShowUploadFileModal(true)
() => setShowUploadFileModal(true),
!activePod
)}
{DriveActionBarItem(
theme,
Expand All @@ -91,7 +95,8 @@ const DriveActionBarMobile: FC<DriveActionBarMobileProps> = ({
intl.get('NEW_FOLDER'),
<CreateFolderLightIcon height="22" />,
<CreateFolderDarkIcon height="22" />,
() => setShowCreateFolderModal(true)
() => setShowCreateFolderModal(true),
!activePod
)}
{showCreatePodModal ? (
<CreatePodModal
Expand Down

0 comments on commit 9450c44

Please sign in to comment.