nexshop-web-contents
Version:
Nexshop Web Contents Project
433 lines (362 loc) • 15.4 kB
JavaScript
import React, {Component} from 'react';
import SceneTitleView from "./scene-title-view";
import {connect} from "react-redux";
import flattenDeep from 'lodash/flattenDeep';
import {bindActionCreators} from "redux";
import {sceneActions, discardConfirmActions} from 'nexshop-web-store';
import {actions as dialogActions} from 'nexshop-web-dialog';
import SceneEditorView from "./scene-editor/scene-editor-view";
import SceneResourceView from "./scene-resource/scene-resource-view";
import {createEmptySceneFromLayouts, createScene} from "../../model-factory/scene";
import ConfirmUpdateContentsItem from "../dialog/confirm-update-contents-item/confirm-update-contents-item"
import {AlertDialog} from "nexshop-web-elements";
const DEFAULT_LAYOUT_ID = '';
const DEFAULT_TAB_ID = 'contents';
const MAX_PANEL_COUNT_WITH_VIDEO = 2;
function getAllPanel(panels, {direction} = 'row') {
const allPanels = [];
for (let i = 0; i < panels.length; i++) {
allPanels.push(panels[i]);
panels[i].parentDirection = direction;
if (panels[i].type === 'group') {
allPanels.push(getAllPanel(panels[i].panels, panels[i]));
}
}
return allPanels;
}
class Scene extends Component {
constructor(props) {
super(props);
let {orientation} = this.props;
let {scene} = props;
let selectedTabId = DEFAULT_TAB_ID;
let selectedLayoutId = DEFAULT_LAYOUT_ID;
let hasFullContent = true;
const isSceneDetail = !scene;
if (isSceneDetail) {
selectedTabId = 'layout';
selectedLayoutId = '1';
scene = createEmptySceneFromLayouts(selectedLayoutId, orientation);
hasFullContent = false;
}
this.state = {
layout: scene,
selectedLayoutId,
selectedTabId,
hasFullContent,
selectedPanelId: '',
draggedOverPanelId: '',
alertVisibility: false,
usedInOthers: false
};
this.selectLayout = this.selectLayout.bind(this);
this.updatePanelWidths = this.updatePanelWidths.bind(this);
this.selectTab = this.selectTab.bind(this);
this.saveScene = this.saveScene.bind(this);
this.checkContentsForPanel = this.checkContentsForPanel.bind(this);
this.checkContentsForGroup = this.checkContentsForGroup.bind(this);
this.isDraggedOverPanel = this.isDraggedOverPanel.bind(this);
this.isSelectedPanel = this.isSelectedPanel.bind(this);
this.setSelectedPanelId = this.setSelectedPanelId.bind(this);
this.setDraggedOverPanelId = this.setDraggedOverPanelId.bind(this);
this.addContentItem = this.addContentItem.bind(this);
this.removeContentItem = this.removeContentItem.bind(this);
this.getPanelGroupRects = this.getPanelGroupRects.bind(this);
this.getPanelRects = this.getPanelRects.bind(this);
this.getPanelSize = this.getPanelSize.bind(this);
this.getWrapperSizeForResolution = this.getWrapperSizeForResolution.bind(this);
this.moveToPreview = this.moveToPreview.bind(this);
this.getCSSWrapperName = this.getCSSWrapperName.bind(this);
this.getRelatedContents = this.getRelatedContents.bind(this);
this.updateFitMode = this.updateFitMode.bind(this);
this.isAddableContentsItem = this.isAddableContentsItem.bind(this);
this.onCloseAlertMessage = this.onCloseAlertMessage.bind(this);
}
isDraggedOverPanel(id) {
return id === this.state.draggedOverPanelId;
}
isSelectedPanel(id) {
return id === this.state.selectedPanelId;
}
setSelectedPanelId(selectedPanelId) {
this.setState({
selectedPanelId
});
}
setDraggedOverPanelId(draggedOverPanelId) {
if (this.state.draggedOverPanelId !== draggedOverPanelId) {
this.setState({
draggedOverPanelId
});
}
}
selectLayout(layoutId) {
this.setState({
layout: createEmptySceneFromLayouts(layoutId, this.props.orientation),
selectedLayoutId: layoutId,
hasFullContent: false,
selectedPanelId: '',
draggedOverPanelId: '',
});
}
addContentItem(panelId, contentItem) {
const newScene = {...this.state.layout};
let panels = flattenDeep(getAllPanel(newScene.panels));
if (contentItem.type === 'video' && !this.isAddableContentsItem(panels)) {
this.setState({
alertVisibility: true,
});
return;
}
let panel = panels.find(panel => panel.id === panelId);
if (!panel.contentItems) {
panel.contentItems = [];
}
panel.contentItems.push(contentItem);
this.setState({
layout: newScene,
hasFullContent: this.checkContentsForGroup(newScene.panels),
});
}
isAddableContentsItem(panels) {
let videoCount = 0;
panels.forEach((panel) => {
let {contentItems: [contentItem = {}] = []} = panel;
if (contentItem.type === 'video') {
videoCount++;
}
});
return videoCount < MAX_PANEL_COUNT_WITH_VIDEO;
}
removeContentItem(panelId) {
const newScene = {...this.state.layout};
let panels = flattenDeep(getAllPanel(newScene.panels));
let panel = panels.find(panel => panel.id === panelId);
delete panel.contentItems;
this.setState({
layout: newScene,
hasFullContent: this.checkContentsForGroup(newScene.panels),
});
}
checkContentsForGroup(panels) {
let resultList = panels.map((panel) => {
if (panel.type === 'panel') {
return this.checkContentsForPanel(panel);
} else {
return this.checkContentsForGroup(panel.panels);
}
});
return resultList.reduce((accumulator, currentValue) => {
return accumulator && currentValue
}, true);
}
checkContentsForPanel(panel) {
return !!panel.contentItems;
}
selectTab(tabId) {
this.setState({
selectedTabId: tabId,
});
}
saveScene() {
this.props.actions.unblockNavigation();
const newScene = {...this.state.layout};
const {sceneId, name, resolution, orientation, breadcrumb, actions: {saveSceneAsync, updateSceneAsync, openDialog}} = this.props;
const relatedContents = this.getRelatedContents(newScene.panels);
const currentFolderId = breadcrumb[breadcrumb.length - 1].id;
const sceneElement = document.getElementById('scene-body');
let sceneFormat = createScene(name, newScene.panels, currentFolderId, resolution, orientation, relatedContents);
if (sceneId) {
updateSceneAsync(sceneFormat, sceneId, sceneElement, resolution, orientation)
.then((response) => {
if (response.occupied) {
openDialog('update-contents-item');
}
});
} else {
saveSceneAsync(sceneFormat, sceneElement, resolution, orientation);
}
}
getRelatedContents(panels) {
let flatPanels = flattenDeep(getAllPanel(panels));
let relatedContentsSet = new Set();
flatPanels.forEach((panel) => {
let contentItems = panel.contentItems;
if (panel.type === 'panel' && contentItems) {
contentItems.forEach((content) => relatedContentsSet.add(content.id));
}
});
return Array.from(relatedContentsSet).map((id) => {
return {id}
});
}
updatePanelWidths() {
const newScene = {...this.state.layout};
let panels = flattenDeep(getAllPanel(newScene.panels)).reverse();
panels.forEach((panel) => {
if (panel.type === 'panel') {
const domRect = document.getElementById(panel.id).getClientRects()[0];
panel.domAttribute = {...panel.domAttribute, ...this.getPanelRects(panel, domRect)};
} else {
panel.domAttribute = {...panel.domAttribute, ...this.getPanelGroupRects(panel)};
}
});
this.setState({
layout: newScene,
hasFullContent: this.checkContentsForGroup(newScene.panels),
})
}
getWrapperSizeForResolution(orientation) {
return orientation === 'horizontal' ? {width: 960, height: 540} : {width: 432, height: 768};
}
getPanelRects({parentDirection}, domRect) {
const basicSize = this.getWrapperSizeForResolution(this.props.orientation);
let width, height, pixelWidth, pixelHeight, size;
width = (domRect.width / basicSize.width) * 100;
height = (domRect.height / basicSize.height) * 100;
pixelWidth = domRect.width;
pixelHeight = domRect.height;
size = this.getPanelSize(parentDirection, domRect);
return {width, height, pixelWidth, pixelHeight, size};
}
getPanelSize(parentDirection, domRect) {
let size = 0;
if (parentDirection === 'row') {
size = domRect.width;
} else {
size = domRect.height;
}
return size;
}
getPanelGroupRects({direction, parentDirection, panels}) {
const basicSize = this.getWrapperSizeForResolution(this.props.orientation);
let pixelHeight, pixelWidth, height, width, size;
if (direction === 'row') {
pixelHeight = panels[0].domAttribute.pixelHeight;
pixelWidth = panels.reduce(
(accum, current) => {
return accum + current.domAttribute.pixelWidth;
}, 0);
} else {
pixelHeight = panels.reduce(
(accum, current) => {
return accum + current.domAttribute.pixelHeight;
}, 0);
pixelWidth = panels[0].domAttribute.pixelWidth;
}
size = this.getPanelSize(parentDirection, {width: pixelWidth, height: pixelHeight});
width = (pixelWidth / basicSize.width) * 100;
height = (pixelHeight / basicSize.height) * 100;
return {pixelHeight, pixelWidth, height, width, size};
}
moveToPreview() {
this.props.actions.unblockNavigation();
const newScene = {...this.state.layout};
const {name, resolution, orientation, actions: {createScenePreview}} = this.props;
const previewWindowRects = document.getElementsByClassName('scene')[0].getClientRects()[0];
const previewWindowSize = {width: previewWindowRects.width, height: previewWindowRects.height};
const sceneLayoutRects = document.getElementsByClassName('layout-wrapper' + this.getCSSWrapperName(orientation))[0].getClientRects()[0];
const sceneLayoutSize = {width: sceneLayoutRects.width - 2, height: sceneLayoutRects.height - 2};
createScenePreview(createScene(name, newScene.panels, resolution, orientation), previewWindowSize, sceneLayoutSize);
setTimeout(() => {
this.props.history.push('/contents/scene-preview');
});
}
getCSSWrapperName(orientation) {
return orientation === 'horizontal' ? '--horizontal' : '--vertical';
}
updateFitMode(fitMode, panelId, contentItemId) {
const newScene = {...this.state.layout};
let panels = flattenDeep(getAllPanel(newScene.panels));
let panel = panels.find(panel => panel.id === panelId);
let contentItem = panel.contentItems.find(contentItem => contentItem.id === contentItemId);
contentItem.fitMode = fitMode;
this.setState({
layout: newScene,
});
}
onCloseAlertMessage() {
this.setState({
selectedPanelId: '',
draggedOverPanelId: '',
alertVisibility: false,
});
}
componentWillMount() {
const {name, resolution, history} = this.props;
if (!name || !resolution) {
history.push('/contents/main');
}
}
componentDidMount() {
this.props.actions.blockNavigation('Your work has not been saved.');
}
componentWillUnmount() {
this.props.actions.unblockNavigation();
}
render() {
const {name = 'scene', orientation = 'vertical'} = this.props;
const {layout, selectedLayoutId, selectedTabId, hasFullContent, alertVisibility} = this.state;
return (
<div className="scene">
<SceneTitleView title={name}
saveScene={this.saveScene} hasFullContent={hasFullContent}
onPreviewClick={this.moveToPreview}
/>
<div className="scene-body">
<SceneEditorView
layout={layout}
orientation={orientation}
updatePanelWidths={this.updatePanelWidths}
selectedTabId={selectedTabId}
isDraggedOverPanel={this.isDraggedOverPanel}
isSelectedPanel={this.isSelectedPanel}
setSelectedPanelId={this.setSelectedPanelId}
setDraggedOverPanelId={this.setDraggedOverPanelId}
addContentItem={this.addContentItem}
getCSSWrapperName={this.getCSSWrapperName}
removeContentItem={this.removeContentItem}
updateFitMode={this.updateFitMode}
/>
<SceneResourceView
orientation={orientation}
getCSSWrapperName={this.getCSSWrapperName}
changeLayout={this.selectLayout}
selectTab={this.selectTab}
selectedTabId={selectedTabId}
selectedLayoutId={selectedLayoutId}
/>
<AlertDialog isOpen={alertVisibility}
icon={'warning'}
message={'You can add up to 2 video files.'}
positiveButtonText={'OK'}
onPositiveClick={this.onCloseAlertMessage}/>
<ConfirmUpdateContentsItem name={name}/>
</div>
</div>
);
}
}
const mapStateToProps = (state) => {
let {scene: {scene, sceneId, name, resolution, orientation, usedIn}, contents: {breadcrumb}} = state;
return {
name,
resolution,
orientation,
scene,
sceneId,
breadcrumb,
usedIn,
};
};
const mapDispatchToProps = (dispatch) => {
const {saveSceneAsync, updateSceneAsync, createScenePreview} = sceneActions;
const {blockNavigation, unblockNavigation} = discardConfirmActions;
const {openDialog} = dialogActions;
return {
actions: bindActionCreators({
saveSceneAsync, updateSceneAsync, createScenePreview, blockNavigation, unblockNavigation, openDialog
}, dispatch)
}
};
export default connect(mapStateToProps, mapDispatchToProps)(Scene);