PromucFlow_constructor/app/client/src/widgets/DocumentViewerWidget/component/index.tsx
CommanderRoot a778a98016
chore: replace deprecated String.prototype.substr() (#11928)
String.prototype.substr() is deprecated (see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/substr) so we replace it with slice() which works similarily but isn't deprecated

Signed-off-by: Tobias Speicher <rootcommander@gmail.com>
2022-04-13 12:30:38 +05:30

221 lines
6.3 KiB
TypeScript

import React, { Suspense, lazy } from "react";
import styled from "styled-components";
import { DocumentViewer } from "react-documents";
import { includes, replace, split, get } from "lodash";
import {
SUPPORTED_EXTENSIONS,
Renderers,
Renderer,
ViewerType,
} from "../constants";
import { retryPromise } from "utils/AppsmithUtils";
import Skeleton from "components/utils/Skeleton";
const DocViewer = lazy(() => retryPromise(() => import("./DocViewer")));
const XlsxViewer = lazy(() => retryPromise(() => import("./XlsxViewer")));
const ErrorWrapper = styled.div`
display: flex;
justify-content: center;
align-items: center;
background: #fff;
`;
const checkUrlExtension = (docUrl: string) => {
// Remove everything to the last slash in URL
let url = docUrl.slice(1 + docUrl.lastIndexOf("/"));
// Break URL at ? and take first part (file name, extension)
url = url.split("?")[0];
// Sometimes URL doesn't have ? but #, so we should aslo do the same for #
url = url.split("#")[0];
// Now we have only filename and extension
const chunkList = url.split(".");
if (chunkList.length > 1) {
const ext = chunkList[chunkList.length - 1];
// check extension is valid or not
const validExtension = SUPPORTED_EXTENSIONS.includes(ext);
return {
hasExtension: true,
validExtension: validExtension,
extension: ext,
};
} else {
// might be preview url
return { hasExtension: false };
}
};
const getBlob = (docUrl: string) => {
// Split into two parts
const parts = docUrl.split(";base64,");
// Hold the content type
const mimeType = parts[0].split(":")[1];
try {
// Decode Base64 string
const decodedData = window.atob(parts[1]);
// Create UNIT8ARRAY of size same as row data length
const uInt8Array = new Uint8Array(decodedData.length);
// Insert all character code into uInt8Array
for (let i = 0; i < decodedData.length; ++i) {
uInt8Array[i] = decodedData.charCodeAt(i);
}
// Return BLOB image after conversion
const blob = new Blob([uInt8Array], { type: mimeType });
return blob;
} catch (error) {
return;
}
};
// get extension from base64
const getFileExtensionFromBase64 = (docUrl: string) => {
let extension = "";
const fileType = docUrl.split(";")[0].split("/")[1];
switch (fileType) {
case "vnd.openxmlformats-officedocument.wordprocessingml.document":
extension = "docx";
break;
case "vnd.openxmlformats-officedocument.spreadsheetml.sheet":
extension = "xlsx";
break;
case "plain":
extension = "txt";
break;
case "pdf":
extension = "pdf";
break;
default:
break;
}
return extension;
};
interface ConfigResponse {
url: string;
blob?: Blob;
viewer: ViewerType;
renderer: Renderer;
errorMessage?: string;
}
export const getDocViewerConfigs = (docUrl: string): ConfigResponse => {
/**
* From user provided document url decide on which viewer to use
* handle different cases for url hosts
* Case 1: Dropbox
* if user exported download url than change that to raw url
* Case 2: Onedrive
* change url to embeded url to show in viewer
* Case 3:
* All other urls will be either Google Preview or urls with file extentions ( e.x. from aws s3 )
* need to verify urls with extention and use default viewer "url viewer" for them
*/
let viewer = "url" as ViewerType;
let url = docUrl;
let blob;
let errorMessage = !!docUrl ? "" : "No document url provided for viewer";
let renderer: Renderer = errorMessage
? Renderers.ERROR
: Renderers.DOCUMENT_VIEWER;
if (docUrl && includes(docUrl, "base64")) {
const extension = getFileExtensionFromBase64(docUrl);
const isValidExtension = SUPPORTED_EXTENSIONS.includes(extension);
if (isValidExtension) {
blob = getBlob(docUrl);
if (blob) {
if (extension === "docx") {
renderer = Renderers.DOCX_VIEWER;
} else if (extension === "xlsx") {
renderer = Renderers.XLSX_VIEWER;
}
} else {
errorMessage = "invalid base64 data";
renderer = Renderers.ERROR;
}
} else {
errorMessage = "Current file type is not supported " + extension;
renderer = Renderers.ERROR;
}
return { blob, url, viewer, errorMessage, renderer };
}
// handled dropbox url
if (includes(url, "dropbox.com")) {
if (includes(url, "?dl=0") || !includes(url, "?raw=1")) {
url = replace(url, "?dl=0", "");
url = url + "?raw=1";
}
return { url, viewer, renderer };
}
// handled onedrive url
if (includes(url, "onedrive.live.com")) {
const onedriveUrl = new URL(url);
const onedriveUrlParamList = split(onedriveUrl.search, /[?&=]+/);
const cid = get(
onedriveUrlParamList,
onedriveUrlParamList.indexOf("cid") + 1,
);
const resid = get(
onedriveUrlParamList,
onedriveUrlParamList.indexOf("id") + 1,
);
url = `https://onedrive.live.com/embed?cid=${cid}&resid=${resid}&em=2`;
return { url, viewer, renderer };
}
// check url extension and if it is supported
const { extension, hasExtension, validExtension } = checkUrlExtension(url);
if (hasExtension) {
if (validExtension) {
if (!(extension === "txt" || extension === "pdf")) {
viewer = "office";
renderer = Renderers.DOCUMENT_VIEWER;
}
} else {
errorMessage = "Current file type is not supported";
renderer = Renderers.ERROR;
}
}
return { url, viewer, errorMessage, renderer };
};
function DocumentViewerComponent(props: DocumentViewerComponentProps) {
const { blob, errorMessage, renderer, url, viewer } = React.useMemo(
() => getDocViewerConfigs(props.docUrl),
[props.docUrl],
);
switch (renderer) {
case Renderers.ERROR:
return <ErrorWrapper>{errorMessage}</ErrorWrapper>;
case Renderers.DOCX_VIEWER:
return (
<Suspense fallback={<Skeleton />}>
<DocViewer blob={blob} />
</Suspense>
);
case Renderers.XLSX_VIEWER:
return (
<Suspense fallback={<Skeleton />}>
<XlsxViewer blob={blob} />
</Suspense>
);
case Renderers.DOCUMENT_VIEWER:
return <DocumentViewer url={url} viewer={viewer} />;
default:
return null;
}
}
export interface DocumentViewerComponentProps {
docUrl: string;
}
export default DocumentViewerComponent;