UNPKG

box-ui-elements-mlh

Version:
1,312 lines (1,113 loc) 47.2 kB
function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); } function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } } function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; } function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; } function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; } function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); } function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); } function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); } function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); } function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } /** * * @file Content Preview Component * @author Box */ import 'regenerator-runtime/runtime'; import * as React from 'react'; import classNames from 'classnames'; import uniqueid from 'lodash/uniqueId'; import throttle from 'lodash/throttle'; import cloneDeep from 'lodash/cloneDeep'; import omit from 'lodash/omit'; import getProp from 'lodash/get'; import flow from 'lodash/flow'; import noop from 'lodash/noop'; import setProp from 'lodash/set'; import Measure from 'react-measure'; import { withRouter } from 'react-router-dom'; import { decode } from '../../utils/keys'; import makeResponsive from '../common/makeResponsive'; import { withNavRouter } from '../common/nav-router'; import Internationalize from '../common/Internationalize'; import AsyncLoad from '../common/async-load'; import TokenService from '../../utils/TokenService'; import { isInputElement, focus } from '../../utils/dom'; import { getTypedFileId } from '../../utils/file'; import { withErrorBoundary } from '../common/error-boundary'; import { withLogger } from '../common/logger'; import { PREVIEW_FIELDS_TO_FETCH } from '../../utils/fields'; import { mark } from '../../utils/performance'; import { withFeatureProvider } from '../common/feature-checking'; import { EVENT_JS_READY } from '../common/logger/constants'; import ReloadNotification from './ReloadNotification'; import API from '../../api'; import PreviewHeader from './preview-header'; import PreviewMask from './PreviewMask'; import PreviewNavigation from './PreviewNavigation'; import { withAnnotations, WithAnnotationsProps, withAnnotatorContext, WithAnnotatorContextProps } from '../common/annotator-context'; import { DEFAULT_HOSTNAME_API, DEFAULT_HOSTNAME_APP, DEFAULT_HOSTNAME_STATIC, DEFAULT_PREVIEW_VERSION, DEFAULT_LOCALE, DEFAULT_PATH_STATIC_PREVIEW, CLIENT_NAME_CONTENT_PREVIEW, ORIGIN_PREVIEW, ORIGIN_CONTENT_PREVIEW, ERROR_CODE_UNKNOWN } from '../../constants'; import '../common/fonts.scss'; import '../common/base.scss'; import './ContentPreview.scss'; var startAtTypes = { page: 'pages' }; var InvalidIdError = new Error('Invalid id for Preview!'); var PREVIEW_LOAD_METRIC_EVENT = 'load'; var MARK_NAME_JS_READY = "".concat(ORIGIN_CONTENT_PREVIEW, "_").concat(EVENT_JS_READY); mark(MARK_NAME_JS_READY); var LoadableSidebar = AsyncLoad({ loader: function loader() { return import( /* webpackMode: "lazy", webpackChunkName: "content-sidebar" */ '../content-sidebar'); } }); var ContentPreview = /*#__PURE__*/function (_React$PureComponent) { _inherits(ContentPreview, _React$PureComponent); var _super = _createSuper(ContentPreview); // Defines a generic type for ContentSidebar, since an import would interfere with code splitting /** * [constructor] * * @return {ContentPreview} */ function ContentPreview(props) { var _this; _classCallCheck(this, ContentPreview); _this = _super.call(this, props); _defineProperty(_assertThisInitialized(_this), "contentSidebar", /*#__PURE__*/React.createRef()); _defineProperty(_assertThisInitialized(_this), "initialState", { canPrint: false, error: undefined, isLoading: true, isReloadNotificationVisible: false, isThumbnailSidebarOpen: false }); _defineProperty(_assertThisInitialized(_this), "onPreviewError", function (_ref) { var error = _ref.error, rest = _objectWithoutProperties(_ref, ["error"]); var _error$code = error.code, code = _error$code === void 0 ? ERROR_CODE_UNKNOWN : _error$code; var onError = _this.props.onError; // In case of error, there should be no thumbnail sidebar to account for _this.setState({ isLoading: false, isThumbnailSidebarOpen: false }); onError(error, code, _objectSpread(_objectSpread({}, rest), {}, { error: error }), ORIGIN_PREVIEW); }); _defineProperty(_assertThisInitialized(_this), "onPreviewMetric", function (previewMetrics) { var logger = _this.props.logger; var event_name = previewMetrics.event_name; var metrics = _objectSpread({}, previewMetrics); // We need to add in the total file fetch time to the file_info_time and value (total) // as preview does not do the files call if (event_name === PREVIEW_LOAD_METRIC_EVENT) { var totalFetchFileTime = _this.getTotalFileFetchTime(); var totalTime = (previewMetrics.value || 0) + totalFetchFileTime; // If an unnatural load time occurs or is invalid, don't log a load event if (!totalTime) { return; } metrics = _objectSpread(_objectSpread({}, previewMetrics), {}, { file_info_time: totalFetchFileTime, value: totalTime }); } logger.onPreviewMetric(metrics); }); _defineProperty(_assertThisInitialized(_this), "onPreviewLoad", function (data) { var _this$props = _this.props, onLoad = _this$props.onLoad, collection = _this$props.collection; var currentIndex = _this.getFileIndex(); var filesToPrefetch = collection.slice(currentIndex + 1, currentIndex + 5); var previewTimeMetrics = getProp(data, 'metrics.time'); var loadData = data; if (previewTimeMetrics) { var totalPreviewMetrics = _this.addFetchFileTimeToPreviewMetrics(previewTimeMetrics); loadData = _objectSpread(_objectSpread({}, loadData), {}, { metrics: _objectSpread(_objectSpread({}, loadData.metrics), {}, { time: totalPreviewMetrics }) }); } onLoad(loadData); _this.setState({ isLoading: false }); _this.focusPreview(); if (_this.preview && filesToPrefetch.length) { _this.prefetch(filesToPrefetch); } _this.handleCanPrint(); }); _defineProperty(_assertThisInitialized(_this), "loadPreview", /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee() { var _this$props2, _this$props2$annotato, activeAnnotationId, enableThumbnailsSidebar, fileOptions, onAnnotatorEvent, onAnnotator, showAnnotationsControls, tokenOrTokenFunction, rest, _this$state, file, selectedVersion, startAt, fileId, fileOpts, token, previewOptions, Preview; return regeneratorRuntime.wrap(function _callee$(_context) { while (1) { switch (_context.prev = _context.next) { case 0: _this$props2 = _this.props, _this$props2$annotato = _this$props2.annotatorState; _this$props2$annotato = _this$props2$annotato === void 0 ? {} : _this$props2$annotato; activeAnnotationId = _this$props2$annotato.activeAnnotationId, enableThumbnailsSidebar = _this$props2.enableThumbnailsSidebar, fileOptions = _this$props2.fileOptions, onAnnotatorEvent = _this$props2.onAnnotatorEvent, onAnnotator = _this$props2.onAnnotator, showAnnotationsControls = _this$props2.showAnnotationsControls, tokenOrTokenFunction = _this$props2.token, rest = _objectWithoutProperties(_this$props2, ["annotatorState", "enableThumbnailsSidebar", "fileOptions", "onAnnotatorEvent", "onAnnotator", "showAnnotationsControls", "token"]); _this$state = _this.state, file = _this$state.file, selectedVersion = _this$state.selectedVersion, startAt = _this$state.startAt; if (!(!_this.isPreviewLibraryLoaded() || !file || !tokenOrTokenFunction)) { _context.next = 6; break; } return _context.abrupt("return"); case 6: fileId = _this.getFileId(file); if (!(fileId !== _this.state.currentFileId)) { _context.next = 9; break; } return _context.abrupt("return"); case 9: fileOpts = _objectSpread({}, fileOptions); token = function token(typedId) { return TokenService.getReadTokens(typedId, tokenOrTokenFunction); }; if (selectedVersion) { setProp(fileOpts, [fileId, 'fileVersionId'], selectedVersion.id); setProp(fileOpts, [fileId, 'currentFileVersionId'], getProp(file, 'file_version.id')); } if (activeAnnotationId) { setProp(fileOpts, [fileId, 'annotations', 'activeId'], activeAnnotationId); } if (startAt) { setProp(fileOpts, [fileId, 'startAt'], startAt); } previewOptions = { container: "#".concat(_this.id, " .bcpr-content"), enableThumbnailsSidebar: enableThumbnailsSidebar, fileOptions: fileOpts, header: 'none', headerElement: "#".concat(_this.id, " .bcpr-PreviewHeader"), showAnnotations: _this.canViewAnnotations(), showAnnotationsControls: showAnnotationsControls, showDownload: _this.canDownload(), showLoading: false, showProgress: false, skipServerUpdate: true, useHotkeys: false }; Preview = global.Box.Preview; _this.preview = new Preview(); _this.preview.addListener('load', _this.onPreviewLoad); _this.preview.addListener('preview_error', _this.onPreviewError); _this.preview.addListener('preview_metric', _this.onPreviewMetric); _this.preview.addListener('thumbnailsOpen', function () { return _this.setState({ isThumbnailSidebarOpen: true }); }); _this.preview.addListener('thumbnailsClose', function () { return _this.setState({ isThumbnailSidebarOpen: false }); }); if (showAnnotationsControls) { _this.preview.addListener('annotator_create', onAnnotator); } _this.preview.updateFileCache([file]); _this.preview.show(file.id, token, _objectSpread(_objectSpread({}, previewOptions), omit(rest, Object.keys(previewOptions)))); case 25: case "end": return _context.stop(); } } }, _callee); }))); _defineProperty(_assertThisInitialized(_this), "loadFileFromStage", function () { if (_this.stagedFile) { _this.setState(_objectSpread(_objectSpread({}, _this.initialState), {}, { file: _this.stagedFile }), function () { _this.stagedFile = undefined; }); } }); _defineProperty(_assertThisInitialized(_this), "closeReloadNotification", function () { _this.setState({ isReloadNotificationVisible: false }); }); _defineProperty(_assertThisInitialized(_this), "onResize", function () { if (_this.preview && _this.preview.getCurrentViewer()) { _this.preview.resize(); } }); _defineProperty(_assertThisInitialized(_this), "fetchFileSuccessCallback", function (file) { _this.fetchFileEndTime = performance.now(); var currentFile = _this.state.file; var isExistingFile = currentFile ? currentFile.id === file.id : false; var isWatermarked = getProp(file, 'watermark_info.is_watermarked', false); // If the file is watermarked or if its a new file, then update the state // In this case preview should reload without prompting the user if (isWatermarked || !isExistingFile) { _this.setState(_objectSpread(_objectSpread({}, _this.initialState), {}, { file: file })); // $FlowFixMe file version and sha1 should exist at this point } else if (currentFile.file_version.sha1 !== file.file_version.sha1) { // If we are already prevewing the file that got updated then show the // user a notification to reload the file only if its sha1 changed _this.stagedFile = file; _this.setState(_objectSpread(_objectSpread({}, _this.initialState), {}, { isReloadNotificationVisible: true })); } }); _defineProperty(_assertThisInitialized(_this), "fetchFileErrorCallback", function (fileError, code) { var onError = _this.props.onError; var errorCode = fileError.code || code; var error = { code: errorCode, message: fileError.message }; _this.setState({ error: error, file: undefined }); onError(fileError, errorCode, { error: fileError }); }); _defineProperty(_assertThisInitialized(_this), "getPreview", function () { var file = _this.state.file; if (!_this.preview || !file) { return null; } return _this.preview; }); _defineProperty(_assertThisInitialized(_this), "getViewer", function () { var preview = _this.getPreview(); var viewer = preview ? preview.getCurrentViewer() : null; return viewer && viewer.isLoaded() && !viewer.isDestroyed() ? viewer : null; }); _defineProperty(_assertThisInitialized(_this), "navigateLeft", function () { var currentIndex = _this.getFileIndex(); var newIndex = currentIndex === 0 ? 0 : currentIndex - 1; if (newIndex !== currentIndex) { _this.navigateToIndex(newIndex); } }); _defineProperty(_assertThisInitialized(_this), "navigateRight", function () { var collection = _this.props.collection; var currentIndex = _this.getFileIndex(); var newIndex = currentIndex === collection.length - 1 ? collection.length - 1 : currentIndex + 1; if (newIndex !== currentIndex) { _this.navigateToIndex(newIndex); } }); _defineProperty(_assertThisInitialized(_this), "download", function () { var onDownload = _this.props.onDownload; var file = _this.state.file; if (_this.preview) { _this.preview.download(); onDownload(cloneDeep(file)); } }); _defineProperty(_assertThisInitialized(_this), "print", function () { if (_this.preview) { _this.preview.print(); } }); _defineProperty(_assertThisInitialized(_this), "onMouseMove", throttle(function () { var viewer = _this.getViewer(); var isPreviewing = !!viewer; var CLASS_NAVIGATION_VISIBILITY = 'bcpr-nav-is-visible'; clearTimeout(_this.mouseMoveTimeoutID); if (!_this.previewContainer) { return; } // Always assume that navigation arrows will be hidden _this.previewContainer.classList.remove(CLASS_NAVIGATION_VISIBILITY); // Only show it if either we aren't previewing or if we are then the viewer // is not blocking the show. If we are previewing then the viewer may choose // to not allow navigation arrows. This is mostly useful for videos since the // navigation arrows may interfere with the settings menu inside video player. if (_this.previewContainer && (!isPreviewing || viewer.allowNavigationArrows())) { _this.previewContainer.classList.add(CLASS_NAVIGATION_VISIBILITY); } _this.mouseMoveTimeoutID = setTimeout(function () { if (_this.previewContainer) { _this.previewContainer.classList.remove(CLASS_NAVIGATION_VISIBILITY); } }, 1500); }, 1000)); _defineProperty(_assertThisInitialized(_this), "onKeyDown", function (event) { var useHotkeys = _this.props.useHotkeys; if (!useHotkeys) { return; } var consumed = false; var key = decode(event); var viewer = _this.getViewer(); // If focus was on an input or if the viewer doesn't exist // then don't bother doing anything further if (!key || !viewer || isInputElement(event.target)) { return; } if (typeof viewer.onKeydown === 'function') { consumed = !!viewer.onKeydown(key, event.nativeEvent); } if (!consumed) { switch (key) { case 'ArrowLeft': _this.navigateLeft(); consumed = true; break; case 'ArrowRight': _this.navigateRight(); consumed = true; break; default: // no-op } } if (consumed) { event.preventDefault(); event.stopPropagation(); } }); _defineProperty(_assertThisInitialized(_this), "onVersionChange", function (version) { var additionalVersionInfo = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; var onVersionChange = _this.props.onVersionChange; _this.updateVersionToCurrent = additionalVersionInfo.updateVersionToCurrent; onVersionChange(version, additionalVersionInfo); _this.setState({ selectedVersion: version }); }); _defineProperty(_assertThisInitialized(_this), "handleAnnotationSelect", function (_ref3) { var file_version = _ref3.file_version, id = _ref3.id, target = _ref3.target; var _target$location = target.location, location = _target$location === void 0 ? {} : _target$location; var _this$state2 = _this.state, file = _this$state2.file, selectedVersion = _this$state2.selectedVersion; var annotationFileVersionId = getProp(file_version, 'id'); var currentFileVersionId = getProp(file, 'file_version.id'); var currentPreviewFileVersionId = getProp(selectedVersion, 'id', currentFileVersionId); var unit = startAtTypes[location.type]; var viewer = _this.getViewer(); if (unit && annotationFileVersionId && annotationFileVersionId !== currentPreviewFileVersionId) { _this.setState({ startAt: { unit: unit, value: location.value } }); } if (viewer) { viewer.emit('scrolltoannotation', { id: id, target: target }); } }); _defineProperty(_assertThisInitialized(_this), "containerRef", function (container) { _this.previewContainer = container; }); var apiHost = props.apiHost, cache = props.cache, _fileId = props.fileId, language = props.language, requestInterceptor = props.requestInterceptor, responseInterceptor = props.responseInterceptor, sharedLink = props.sharedLink, sharedLinkPassword = props.sharedLinkPassword, _token = props.token; _this.id = uniqueid('bcpr_'); _this.api = new API({ apiHost: apiHost, cache: cache, clientName: CLIENT_NAME_CONTENT_PREVIEW, language: language, requestInterceptor: requestInterceptor, responseInterceptor: responseInterceptor, sharedLink: sharedLink, sharedLinkPassword: sharedLinkPassword, token: _token }); _this.state = _objectSpread(_objectSpread({}, _this.initialState), {}, { currentFileId: _fileId, // eslint-disable-next-line react/no-unused-state prevFileIdProp: _fileId }); var _logger = props.logger; _logger.onReadyMetric({ endMarkName: MARK_NAME_JS_READY }); return _this; } /** * Cleanup * * @return {void} */ _createClass(ContentPreview, [{ key: "componentWillUnmount", value: function componentWillUnmount() { // Don't destroy the cache while unmounting this.api.destroy(false); this.destroyPreview(); } /** * Cleans up the preview instance */ }, { key: "destroyPreview", value: function destroyPreview() { var shouldReset = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true; var onPreviewDestroy = this.props.onPreviewDestroy; if (this.preview) { this.preview.destroy(); this.preview.removeAllListeners(); this.preview = undefined; onPreviewDestroy(shouldReset); } } /** * Destroys api instances with caches * * @private * @return {void} */ }, { key: "clearCache", value: function clearCache() { this.api.destroy(true); } /** * Once the component mounts, load Preview assets and fetch file info. * * @return {void} */ }, { key: "componentDidMount", value: function componentDidMount() { this.loadStylesheet(); this.loadScript(); this.fetchFile(this.state.currentFileId); this.focusPreview(); } }, { key: "componentDidUpdate", value: /** * After component updates, load Preview if appropriate. * * @return {void} */ function componentDidUpdate(prevProps, prevState) { var token = this.props.token; var prevToken = prevProps.token; var currentFileId = this.state.currentFileId; var hasFileIdChanged = prevState.currentFileId !== currentFileId; var hasTokenChanged = prevToken !== token; if (hasFileIdChanged) { this.destroyPreview(); this.setState({ isLoading: true, selectedVersion: undefined }); this.fetchFile(currentFileId); } else if (this.shouldLoadPreview(prevState)) { this.destroyPreview(false); this.setState({ isLoading: true }); this.loadPreview(); } else if (hasTokenChanged) { this.updatePreviewToken(); } } /** * Updates the access token used by preview library * * @param {boolean} shouldReload - true if preview should be reloaded */ }, { key: "updatePreviewToken", value: function updatePreviewToken() { var shouldReload = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false; if (this.preview) { this.preview.updateToken(this.props.token, shouldReload); } } /** * Returns whether or not preview should be loaded. * * @param {State} prevState - Previous state * @return {boolean} */ }, { key: "shouldLoadPreview", value: function shouldLoadPreview(prevState) { var _this$state3 = this.state, file = _this$state3.file, selectedVersion = _this$state3.selectedVersion; var prevFile = prevState.file, prevSelectedVersion = prevState.selectedVersion; var prevSelectedVersionId = getProp(prevSelectedVersion, 'id'); var selectedVersionId = getProp(selectedVersion, 'id'); var prevFileVersionId = getProp(prevFile, 'file_version.id'); var fileVersionId = getProp(file, 'file_version.id'); var loadPreview = false; if (selectedVersionId !== prevSelectedVersionId) { var isPreviousCurrent = fileVersionId === prevSelectedVersionId || !prevSelectedVersionId; var isSelectedCurrent = fileVersionId === selectedVersionId || !selectedVersionId; // Load preview if the user has selected a non-current version of the file loadPreview = !isPreviousCurrent || !isSelectedCurrent; } else if (fileVersionId && prevFileVersionId) { // Load preview if the file's current version ID has changed loadPreview = fileVersionId !== prevFileVersionId; } else { // Load preview if file object has newly been populated in state loadPreview = !prevFile && !!file; } return loadPreview; } /** * Returns preview asset urls * * @return {string} base url */ }, { key: "getBasePath", value: function getBasePath(asset) { var _this$props3 = this.props, staticHost = _this$props3.staticHost, staticPath = _this$props3.staticPath, language = _this$props3.language, previewLibraryVersion = _this$props3.previewLibraryVersion; var path = "".concat(staticPath, "/").concat(previewLibraryVersion, "/").concat(language, "/").concat(asset); var suffix = staticHost.endsWith('/') ? path : "/".concat(path); return "".concat(staticHost).concat(suffix); } /** * Determines if preview assets are loaded * * @return {boolean} true if preview is loaded */ }, { key: "isPreviewLibraryLoaded", value: function isPreviewLibraryLoaded() { return !!global.Box && !!global.Box.Preview; } /** * Loads external css by appending a <link> element * * @return {void} */ }, { key: "loadStylesheet", value: function loadStylesheet() { var _document = document, head = _document.head; var url = this.getBasePath('preview.css'); if (!head || head.querySelector("link[rel=\"stylesheet\"][href=\"".concat(url, "\"]"))) { return; } var link = document.createElement('link'); link.rel = 'stylesheet'; link.type = 'text/css'; link.href = url; head.appendChild(link); } /** * Loads external script by appending a <script> element * * @return {void} */ }, { key: "loadScript", value: function loadScript() { var _document2 = document, head = _document2.head; var url = this.getBasePath('preview.js'); if (!head || this.isPreviewLibraryLoaded()) { return; } var previewScript = head.querySelector("script[src=\"".concat(url, "\"]")); if (previewScript) { return; } var script = document.createElement('script'); script.src = url; script.addEventListener('load', this.loadPreview); head.appendChild(script); } /** * Focuses the preview on load. * * @return {void} */ }, { key: "focusPreview", value: function focusPreview() { var _this$props4 = this.props, autoFocus = _this$props4.autoFocus, getInnerRef = _this$props4.getInnerRef; if (autoFocus && !isInputElement(document.activeElement)) { focus(getInnerRef()); } } /** * Updates preview cache and prefetches a file * * @param {BoxItem} file - file to prefetch * @return {void} */ }, { key: "updatePreviewCacheAndPrefetch", value: function updatePreviewCacheAndPrefetch(file, token) { if (!this.preview || !file || !file.id) { return; } this.preview.updateFileCache([file]); this.preview.prefetch({ fileId: file.id, token: token }); } /** * Gets the file id * * @param {string|BoxItem} file - box file or file id * @return {string} file id */ }, { key: "getFileId", value: function getFileId(file) { if (typeof file === 'string') { return file; } if (_typeof(file) === 'object' && !!file.id) { return file.id; } throw InvalidIdError; } /** * Prefetches the next few preview files if any * * @param {Array<string|BoxItem>} files - files to prefetch * @return {void} */ }, { key: "prefetch", value: function () { var _prefetch = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee2(files) { var _this2 = this; var token, typedIds; return regeneratorRuntime.wrap(function _callee2$(_context2) { while (1) { switch (_context2.prev = _context2.next) { case 0: token = this.props.token; typedIds = files.map(function (file) { return getTypedFileId(_this2.getFileId(file)); }); _context2.next = 4; return TokenService.cacheTokens(typedIds, token); case 4: files.forEach(function (file) { var fileId = _this2.getFileId(file); _this2.fetchFile(fileId, noop, noop, { refreshCache: false }); }); case 5: case "end": return _context2.stop(); } } }, _callee2, this); })); function prefetch(_x) { return _prefetch.apply(this, arguments); } return prefetch; }() /** * Calculates the total file fetch time * * @return {number} the total fetch time */ }, { key: "getTotalFileFetchTime", value: function getTotalFileFetchTime() { if (!this.fetchFileStartTime || !this.fetchFileEndTime) { return 0; } return Math.round(this.fetchFileEndTime - this.fetchFileStartTime); } /** * Handler for 'preview_error' preview event * * @param {PreviewLibraryError} previewError - the error data emitted from preview * @return {void} */ }, { key: "addFetchFileTimeToPreviewMetrics", value: /** * Adds in the file fetch time to the preview metrics * * @param {Object} previewTimeMetrics - the preview time metrics * @return {Object} the preview time metrics merged with the files call time */ function addFetchFileTimeToPreviewMetrics(previewTimeMetrics) { var totalFetchFileTime = this.getTotalFileFetchTime(); var rendering = previewTimeMetrics.rendering, conversion = previewTimeMetrics.conversion, preload = previewTimeMetrics.preload; // We need to add in the total file fetch time to the rendering and total as preview // does not do the files call. In the case the file is in the process of // being converted, we need to add to conversion instead of the render var totalConversion = conversion; var totalRendering = rendering; var totalPreload = preload; if (conversion) { totalConversion += totalFetchFileTime; } else { totalRendering += totalFetchFileTime; } if (totalPreload) { // Preload is optional, depending on file type totalPreload += totalFetchFileTime; } var previewMetrics = { conversion: totalConversion, rendering: totalRendering, total: totalRendering + totalConversion, preload: totalPreload }; return previewMetrics; } /** * onLoad function for preview * * @return {void} */ }, { key: "canDownload", value: /** * Returns whether file can be downloaded based on file properties, permissions, and user-defined options. * * @return {boolean} */ function canDownload() { var canDownload = this.props.canDownload; var file = this.state.file; var isFileDownloadable = getProp(file, 'permissions.can_download', false) && getProp(file, 'is_download_available', false); return isFileDownloadable && !!canDownload; } /** * Returns whether file can be annotated based on permissions * * @return {boolean} */ }, { key: "canAnnotate", value: function canAnnotate() { var showAnnotations = this.props.showAnnotations; var file = this.state.file; var isFileAnnotatable = getProp(file, 'permissions.can_annotate', false); return !!showAnnotations && isFileAnnotatable; } /** * Returns whether a preview should render annotations based on permissions * * @return {boolean} */ }, { key: "canViewAnnotations", value: function canViewAnnotations() { var showAnnotations = this.props.showAnnotations; var file = this.state.file; var hasViewAllPermissions = getProp(file, 'permissions.can_view_annotations_all', false); var hasViewSelfPermissions = getProp(file, 'permissions.can_view_annotations_self', false); return !!showAnnotations && (this.canAnnotate() || hasViewAllPermissions || hasViewSelfPermissions); } }, { key: "handleCanPrint", value: function handleCanPrint() { var preview = this.getPreview(); this.setState({ canPrint: !!preview && (!preview.canPrint || preview.canPrint()) }); } /** * Loads preview in the component using the preview library. * * @return {void} */ }, { key: "fetchFile", value: /** * Fetches a file * * @param {string} id file id * @param {Function|void} [successCallback] - Callback after file is fetched * @param {Function|void} [errorCallback] - Callback after error * @param {Object|void} [fetchOptions] - Fetch options * @return {void} */ function fetchFile(id, successCallback, errorCallback) { var fetchOptions = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {}; if (!id) { return; } this.fetchFileStartTime = performance.now(); this.fetchFileEndTime = null; this.api.getFileAPI().getFile(id, successCallback || this.fetchFileSuccessCallback, errorCallback || this.fetchFileErrorCallback, _objectSpread(_objectSpread({}, fetchOptions), {}, { fields: PREVIEW_FIELDS_TO_FETCH })); } /** * Returns the preview instance * * @return {Preview} current instance of preview */ }, { key: "getFileIndex", value: /** * Finds the index of current file inside the collection * * @return {number} -1 if not indexed */ function getFileIndex() { var currentFileId = this.state.currentFileId; var collection = this.props.collection; if (!currentFileId || collection.length < 2) { return -1; } return collection.findIndex(function (item) { if (typeof item === 'string') { return item === currentFileId; } return item.id === currentFileId; }); } /** * Shows a preview of a file at the specified index in the current collection. * * @public * @param {number} index - Index of file to preview * @return {void} */ }, { key: "navigateToIndex", value: function navigateToIndex(index) { var _this$props5 = this.props, collection = _this$props5.collection, onNavigate = _this$props5.onNavigate; var length = collection.length; if (length < 2 || index < 0 || index > length - 1) { return; } var fileOrId = collection[index]; var fileId = _typeof(fileOrId) === 'object' ? fileOrId.id || '' : fileOrId; this.setState({ currentFileId: fileId }, function () { // Execute navigation callback onNavigate(fileId); }); } /** * Shows a preview of the previous file. * * @public * @return {void} */ }, { key: "refreshSidebar", value: /** * Refreshes the content sidebar panel * * @return {void} */ function refreshSidebar() { var contentSidebar = this.contentSidebar.current; if (contentSidebar) { contentSidebar.refresh(); } } /** * Renders the file preview * * @inheritdoc * @return {Element} */ }, { key: "render", value: function render() { var _this$props6 = this.props, apiHost = _this$props6.apiHost, collection = _this$props6.collection, token = _this$props6.token, language = _this$props6.language, messages = _this$props6.messages, className = _this$props6.className, contentSidebarProps = _this$props6.contentSidebarProps, contentOpenWithProps = _this$props6.contentOpenWithProps, hasHeader = _this$props6.hasHeader, history = _this$props6.history, isLarge = _this$props6.isLarge, isVeryLarge = _this$props6.isVeryLarge, logoUrl = _this$props6.logoUrl, onClose = _this$props6.onClose, measureRef = _this$props6.measureRef, sharedLink = _this$props6.sharedLink, sharedLinkPassword = _this$props6.sharedLinkPassword, requestInterceptor = _this$props6.requestInterceptor, responseInterceptor = _this$props6.responseInterceptor; var _this$state4 = this.state, canPrint = _this$state4.canPrint, currentFileId = _this$state4.currentFileId, error = _this$state4.error, file = _this$state4.file, isLoading = _this$state4.isLoading, isReloadNotificationVisible = _this$state4.isReloadNotificationVisible, isThumbnailSidebarOpen = _this$state4.isThumbnailSidebarOpen, selectedVersion = _this$state4.selectedVersion; var styleClassName = classNames('be bcpr', { 'bcpr-thumbnails-open': isThumbnailSidebarOpen }, className); if (!currentFileId) { return null; } var errorCode = getProp(error, 'code'); var currentExtension = getProp(file, 'id') === currentFileId ? getProp(file, 'extension') : ''; var currentVersionId = getProp(file, 'file_version.id'); var selectedVersionId = getProp(selectedVersion, 'id', currentVersionId); var onHeaderClose = currentVersionId === selectedVersionId ? onClose : this.updateVersionToCurrent; /* eslint-disable jsx-a11y/no-static-element-interactions */ /* eslint-disable jsx-a11y/no-noninteractive-tabindex */ return /*#__PURE__*/React.createElement(Internationalize, { language: language, messages: messages }, /*#__PURE__*/React.createElement("div", { id: this.id, className: styleClassName, ref: measureRef, onKeyDown: this.onKeyDown, tabIndex: 0 }, hasHeader && /*#__PURE__*/React.createElement(PreviewHeader, { file: file, logoUrl: logoUrl, token: token, onClose: onHeaderClose, onPrint: this.print, canDownload: this.canDownload(), canPrint: canPrint, onDownload: this.download, contentOpenWithProps: contentOpenWithProps, canAnnotate: this.canAnnotate(), selectedVersion: selectedVersion }), /*#__PURE__*/React.createElement("div", { className: "bcpr-body" }, /*#__PURE__*/React.createElement("div", { className: "bcpr-container", onMouseMove: this.onMouseMove, ref: this.containerRef }, file && /*#__PURE__*/React.createElement(Measure, { bounds: true, onResize: this.onResize }, function (_ref4) { var previewRef = _ref4.measureRef; return /*#__PURE__*/React.createElement("div", { ref: previewRef, className: "bcpr-content" }); }), /*#__PURE__*/React.createElement(PreviewMask, { errorCode: errorCode, extension: currentExtension, isLoading: isLoading }), /*#__PURE__*/React.createElement(PreviewNavigation, { collection: collection, currentIndex: this.getFileIndex(), onNavigateLeft: this.navigateLeft, onNavigateRight: this.navigateRight })), file && /*#__PURE__*/React.createElement(LoadableSidebar, _extends({}, contentSidebarProps, { apiHost: apiHost, token: token, cache: this.api.getCache(), fileId: currentFileId, getPreview: this.getPreview, getViewer: this.getViewer, history: history, isDefaultOpen: isLarge || isVeryLarge, language: language, ref: this.contentSidebar, sharedLink: sharedLink, sharedLinkPassword: sharedLinkPassword, requestInterceptor: requestInterceptor, responseInterceptor: responseInterceptor, onAnnotationSelect: this.handleAnnotationSelect, onVersionChange: this.onVersionChange }))), isReloadNotificationVisible && /*#__PURE__*/React.createElement(ReloadNotification, { onClose: this.closeReloadNotification, onClick: this.loadFileFromStage }))); /* eslint-enable jsx-a11y/no-static-element-interactions */ /* eslint-enable jsx-a11y/no-noninteractive-tabindex */ } }], [{ key: "getDerivedStateFromProps", value: function getDerivedStateFromProps(props, state) { var fileId = props.fileId; if (fileId !== state.prevFileIdProp) { return { currentFileId: fileId, prevFileIdProp: fileId }; } return null; } }]); return ContentPreview; }(React.PureComponent); _defineProperty(ContentPreview, "defaultProps", { apiHost: DEFAULT_HOSTNAME_API, appHost: DEFAULT_HOSTNAME_APP, autoFocus: false, canDownload: true, className: '', collection: [], contentOpenWithProps: {}, contentSidebarProps: {}, enableThumbnailsSidebar: false, hasHeader: false, language: DEFAULT_LOCALE, onAnnotator: noop, onAnnotatorEvent: noop, onDownload: noop, onError: noop, onLoad: noop, onNavigate: noop, onPreviewDestroy: noop, onVersionChange: noop, previewLibraryVersion: DEFAULT_PREVIEW_VERSION, showAnnotations: false, staticHost: DEFAULT_HOSTNAME_STATIC, staticPath: DEFAULT_PATH_STATIC_PREVIEW, useHotkeys: true }); export { ContentPreview as ContentPreviewComponent }; export default flow([makeResponsive, withAnnotatorContext, withAnnotations, withRouter, withNavRouter, withFeatureProvider, withLogger(ORIGIN_CONTENT_PREVIEW), withErrorBoundary(ORIGIN_CONTENT_PREVIEW)])(ContentPreview); //# sourceMappingURL=ContentPreview.js.map