UNPKG

box-ui-elements

Version:
354 lines (302 loc) • 12.1 kB
/** * @flow * @file Versions Sidebar container * @author Box */ import * as React from 'react'; import flow from 'lodash/flow'; import getProp from 'lodash/get'; import merge from 'lodash/merge'; import noop from 'lodash/noop'; import { generatePath } from 'react-router-dom'; import type { Match, RouterHistory } from 'react-router-dom'; import type { MessageDescriptor } from 'react-intl'; import { withFeatureConsumer, isFeatureEnabled } from '../../common/feature-checking'; import API from '../../../api'; import { FIELD_METADATA_ARCHIVE } from '../../../constants'; import messages from './messages'; import openUrlInsideIframe from '../../../utils/iframe'; import StaticVersionsSidebar from './StaticVersionSidebar'; import VersionsSidebar from './VersionsSidebar'; import VersionsSidebarAPI from './VersionsSidebarAPI'; import { withAPIContext } from '../../common/api-context'; import { withRouterIfEnabled } from '../../common/routing'; import type { FeatureConfig } from '../../common/feature-checking'; import type { VersionActionCallback, VersionChangeCallback, SidebarLoadCallback } from './flowTypes'; import type { BoxItemVersion, BoxItem, FileVersions } from '../../../common/types/core'; import { ViewType, type ViewTypeValues, type InternalSidebarNavigation, type InternalSidebarNavigationHandler, } from '../../common/types/SidebarNavigation'; type Props = { api: API, features: FeatureConfig, fileId: string, hasSidebarInitialized?: boolean, history?: RouterHistory, internalSidebarNavigation?: InternalSidebarNavigation, internalSidebarNavigationHandler?: InternalSidebarNavigationHandler, match: Match, onLoad: SidebarLoadCallback, onUpgradeClick?: () => void, onVersionChange: VersionChangeCallback, onVersionDelete: VersionActionCallback, onVersionDownload: VersionActionCallback, onVersionPreview: VersionActionCallback, onVersionPromote: VersionActionCallback, onVersionRestore: VersionActionCallback, parentName: ViewTypeValues, routerDisabled?: boolean, versionId?: string, }; type State = { error?: MessageDescriptor, isArchived: boolean, isLoading: boolean, isWatermarked: boolean, versionCount: number, versionLimit: number, versions: Array<BoxItemVersion>, }; class VersionsSidebarContainer extends React.Component<Props, State> { static defaultProps = { onLoad: noop, onVersionChange: noop, onVersionDelete: noop, onVersionDownload: noop, onVersionPreview: noop, onVersionPromote: noop, onVersionRestore: noop, parentName: ViewType.DETAILS, }; api: VersionsSidebarAPI; props: Props; state: State = { isArchived: false, isLoading: true, isWatermarked: false, versionCount: Infinity, versionLimit: Infinity, versions: [], }; window: any = window; componentDidMount() { const { onLoad } = this.props; this.initialize(); this.fetchData().then(() => { onLoad({ component: 'preview', feature: 'versions' }); }); } componentDidUpdate({ fileId: prevFileId, versionId: prevVersionId }: Props) { const { fileId, versionId } = this.props; if (fileId !== prevFileId) { this.refresh(); } if (versionId !== prevVersionId) { this.verifyVersion(); } } handleActionDelete = (versionId: string): Promise<void> => { this.setState({ isLoading: true }); return this.api .deleteVersion(this.findVersion(versionId)) .then(() => this.api.fetchVersion(versionId)) .then(this.handleDeleteSuccess) .then(() => this.props.onVersionDelete(versionId)) .catch(() => this.handleActionError(messages.versionActionDeleteError)); }; handleActionDownload = (versionId: string): Promise<void> => { return this.api .fetchDownloadUrl(this.findVersion(versionId)) .then(openUrlInsideIframe) .then(() => this.props.onVersionDownload(versionId)) .catch(() => this.handleActionError(messages.versionActionDownloadError)); }; handleActionPreview = (versionId: string): void => { this.updateVersion(versionId); this.props.onVersionPreview(versionId); }; handleActionPromote = (versionId: string): Promise<void> => { this.setState({ isLoading: true }); return this.api .promoteVersion(this.findVersion(versionId)) .then(this.api.fetchData) .then(this.handleFetchSuccess) .then(this.handlePromoteSuccess) .then(() => this.props.onVersionPromote(versionId)) .catch(() => this.handleActionError(messages.versionActionPromoteError)); }; handleActionRestore = (versionId: string): Promise<void> => { this.setState({ isLoading: true }); return this.api .restoreVersion(this.findVersion(versionId)) .then(() => this.api.fetchVersion(versionId)) .then(this.handleRestoreSuccess) .then(() => this.props.onVersionRestore(versionId)) .catch(() => this.handleActionError(messages.versionActionRestoreError)); }; handleActionError = (message: MessageDescriptor): void => { this.setState({ error: message, isLoading: false, }); }; handleDeleteSuccess = (data: BoxItemVersion): void => { const { versionId: selectedVersionId } = this.props; const { id: versionId } = data; this.mergeResponse(data); // Bump the user to the current version if they deleted their selected version if (versionId === selectedVersionId) { this.updateVersionToCurrent(); } }; handleRestoreSuccess = (data: BoxItemVersion): void => { this.mergeResponse(data); }; handleFetchError = (): void => { this.setState({ error: messages.versionFetchError, isArchived: false, isLoading: false, isWatermarked: false, versionCount: 0, versions: [], }); }; handleFetchSuccess = ([fileResponse, versionsResponse]: [BoxItem, FileVersions]): [BoxItem, FileVersions] => { const { api } = this.props; const { version_limit } = fileResponse; const isArchived = !!getProp(fileResponse, FIELD_METADATA_ARCHIVE); const isWatermarked = getProp(fileResponse, 'watermark_info.is_watermarked', false); const versionLimit = version_limit !== null && version_limit !== undefined ? version_limit : Infinity; const versionsWithPermissions = api.getVersionsAPI(false).addPermissions(versionsResponse, fileResponse) || {}; const { entries: versions, total_count: versionCount } = versionsWithPermissions; this.setState( { error: undefined, isArchived, isLoading: false, isWatermarked, versionCount, versionLimit, versions: this.sortVersions(versions), }, this.verifyVersion, ); return [fileResponse, versionsResponse]; }; handlePromoteSuccess = ([file]: [BoxItem, FileVersions]): void => { const { file_version: fileVersion } = file; if (fileVersion) { this.updateVersion(fileVersion.id); } }; initialize = (): void => { const { api, features, fileId }: Props = this.props; const isArchiveFeatureEnabled = isFeatureEnabled(features, 'contentSidebar.archive.enabled'); this.api = new VersionsSidebarAPI({ api, fileId, isArchiveFeatureEnabled }); }; fetchData = (): Promise<?[BoxItem, FileVersions]> => { return this.api.fetchData().then(this.handleFetchSuccess).catch(this.handleFetchError); }; findVersion = (versionId: ?string): ?BoxItemVersion => { const { versions } = this.state; return versions.find(version => version.id === versionId); }; getCurrentVersionId = (): ?string => { const { versions } = this.state; return versions[0] ? versions[0].id : null; }; mergeVersions = (newVersion: BoxItemVersion): Array<BoxItemVersion> => { const { versions } = this.state; const newVersionId = newVersion ? newVersion.id : ''; return versions.map(version => (version.id === newVersionId ? merge({ ...version }, newVersion) : version)); }; mergeResponse = (data: BoxItemVersion): void => { const newVersions = this.mergeVersions(data); this.setState({ error: undefined, isLoading: false, versions: newVersions, }); }; refresh(): void { this.initialize(); this.setState({ isLoading: true }, this.fetchData); } sortVersions(versions?: Array<BoxItemVersion> = []): Array<BoxItemVersion> { return [...versions].sort((a, b) => Date.parse(b.created_at) - Date.parse(a.created_at)); } updateVersion = (versionId?: ?string): void => { const { history, match, routerDisabled, internalSidebarNavigationHandler, internalSidebarNavigation } = this.props; if (routerDisabled && internalSidebarNavigationHandler) { const navigationUpdate: InternalSidebarNavigation = { ...internalSidebarNavigation }; if (versionId) { navigationUpdate.versionId = versionId; } else { delete navigationUpdate.versionId; } internalSidebarNavigationHandler(navigationUpdate); } else if (history) { history.push(generatePath(match.path, { ...match.params, versionId })); } }; updateVersionToCurrent = (): void => { this.updateVersion(this.getCurrentVersionId()); }; verifyVersion = () => { const { onVersionChange, versionId } = this.props; const selectedVersion = this.findVersion(versionId); if (selectedVersion) { onVersionChange(selectedVersion, { currentVersionId: this.getCurrentVersionId(), updateVersionToCurrent: this.updateVersionToCurrent, }); } else { this.updateVersionToCurrent(); } }; render() { const { fileId, parentName, onUpgradeClick, routerDisabled, internalSidebarNavigation, internalSidebarNavigationHandler, } = this.props; if (onUpgradeClick) { return ( <StaticVersionsSidebar onUpgradeClick={onUpgradeClick} parentName={parentName} routerDisabled={routerDisabled} internalSidebarNavigation={internalSidebarNavigation} internalSidebarNavigationHandler={internalSidebarNavigationHandler} {...this.state} /> ); } return ( <VersionsSidebar fileId={fileId} internalSidebarNavigation={internalSidebarNavigation} internalSidebarNavigationHandler={internalSidebarNavigationHandler} onDelete={this.handleActionDelete} onDownload={this.handleActionDownload} onPreview={this.handleActionPreview} onPromote={this.handleActionPromote} onRestore={this.handleActionRestore} parentName={parentName} routerDisabled={routerDisabled} {...this.state} /> ); } } export type VersionsSidebarProps = Props; export { VersionsSidebarContainer as VersionsSidebarContainerComponent }; export default flow([withRouterIfEnabled, withAPIContext, withFeatureConsumer])(VersionsSidebarContainer);