2019-03-13 13:53:40 +01:00
|
|
|
import { AssetsTable } from './assetstable/Table'
|
|
|
|
import { TasksTable } from './taskstable/Table'
|
|
|
|
import { ShotsTable } from './shotstable/Table'
|
|
|
|
import './detailedview/Viewer'
|
|
|
|
const BrowserHistoryState = pillar.vuecomponents.mixins.BrowserHistoryState;
|
|
|
|
const StateSaveMode = pillar.vuecomponents.mixins.StateSaveMode;
|
|
|
|
|
|
|
|
const TEMPLATE =`
|
|
|
|
<div class="attract-app">
|
|
|
|
<div id="col_main">
|
|
|
|
<component
|
|
|
|
:is="tableComponentName"
|
2019-03-15 10:18:23 +01:00
|
|
|
:project="project"
|
2019-03-13 13:53:40 +01:00
|
|
|
:selectedIds="selectedIds"
|
|
|
|
:canChangeSelectionCB="canChangeSelectionCB"
|
2019-03-28 10:29:13 +01:00
|
|
|
:componentState="initialTableState"
|
2019-03-13 13:53:40 +01:00
|
|
|
@selectItemsChanged="onSelectItemsChanged"
|
|
|
|
@isInitialized="onTableInitialized"
|
2019-03-28 10:29:13 +01:00
|
|
|
@componentStateChanged="onTableStateChanged"
|
2019-03-13 13:53:40 +01:00
|
|
|
/>
|
|
|
|
</div>
|
|
|
|
<div class="col-splitter"/>
|
|
|
|
<attract-detailed-view id="col_right"
|
|
|
|
:items="selectedItems"
|
|
|
|
:project="project"
|
|
|
|
:contextType="contextType"
|
|
|
|
@objects-are-edited="onEditingObjects"
|
|
|
|
/>
|
|
|
|
</div>
|
|
|
|
`;
|
|
|
|
|
2019-03-28 10:29:13 +01:00
|
|
|
class ComponentState {
|
|
|
|
/**
|
|
|
|
* Serializable state of this component.
|
|
|
|
*
|
|
|
|
* @param {Object} tableState
|
|
|
|
*/
|
|
|
|
constructor(tableState) {
|
|
|
|
this.tableState = tableState;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
|
|
* Component wrapping a table for selecting attract_task/asset/shot nodes, and a editor to edit the selected node(s).
|
|
|
|
* Selected row filters and visible columns are stored in localStorage per project/context. This makes the settings
|
|
|
|
* sticky between sessions in the same browser.
|
|
|
|
* Selected nodes are stored in window.history. This makes it possible to move back/forward in browser and the selection
|
|
|
|
* will change accordingly.
|
|
|
|
*/
|
2019-03-13 13:53:40 +01:00
|
|
|
Vue.component('attract-app', {
|
|
|
|
template: TEMPLATE,
|
|
|
|
mixins: [BrowserHistoryState],
|
|
|
|
props: {
|
|
|
|
projectId: String,
|
|
|
|
selectedIds: {
|
|
|
|
type: Array,
|
|
|
|
default: []
|
|
|
|
},
|
|
|
|
contextType: {
|
|
|
|
type: String,
|
|
|
|
default: 'shots',
|
|
|
|
}
|
|
|
|
},
|
|
|
|
data() {
|
|
|
|
return {
|
|
|
|
selectedItems: [],
|
|
|
|
isEditing: false,
|
|
|
|
isTableInited: false,
|
|
|
|
project: null
|
|
|
|
}
|
|
|
|
},
|
|
|
|
created() {
|
|
|
|
pillar.api.thenGetProject(this.projectId)
|
|
|
|
.then((project) =>{
|
|
|
|
this.project = project;
|
|
|
|
});
|
|
|
|
},
|
|
|
|
computed: {
|
|
|
|
selectedNames() {
|
|
|
|
return this.selectedItems.map(it => it.name);
|
|
|
|
},
|
|
|
|
tableComponentName() {
|
2019-03-15 10:18:23 +01:00
|
|
|
if(!this.project) return '';
|
2019-03-13 13:53:40 +01:00
|
|
|
switch (this.contextType) {
|
|
|
|
case 'assets': return AssetsTable.options.name;
|
|
|
|
case 'tasks': return TasksTable.options.name;
|
|
|
|
case 'shots': return ShotsTable.options.name;
|
|
|
|
default:
|
|
|
|
console.log('Unknown context type', this.contextType);
|
|
|
|
return ShotsTable.$options.name;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
/**
|
|
|
|
* @override BrowserHistoryState
|
|
|
|
*/
|
|
|
|
browserHistoryState() {
|
|
|
|
if(this.isTableInited) {
|
|
|
|
return {
|
|
|
|
'selectedIds': this.selectedIds
|
|
|
|
};
|
|
|
|
} else {
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
},
|
|
|
|
/**
|
|
|
|
* @override BrowserHistoryState
|
|
|
|
*/
|
|
|
|
historyStateUrl() {
|
|
|
|
let projectUrl = ProjectUtils.projectUrl();
|
|
|
|
if(this.selectedItems.length !== 1) {
|
|
|
|
return `/attract/${projectUrl}/${this.contextType}/`;
|
|
|
|
} else {
|
|
|
|
let selected = this.selectedItems[0];
|
|
|
|
let node_type = selected.node_type;
|
|
|
|
if (node_type === 'attract_task' && this.contextType !== 'tasks') {
|
|
|
|
return `/attract/${projectUrl}/${this.contextType}/with-task/${selected._id}`;
|
|
|
|
} else {
|
|
|
|
return `/attract/${projectUrl}/${this.contextType}/${selected._id}`;
|
|
|
|
}
|
|
|
|
}
|
2019-03-28 10:29:13 +01:00
|
|
|
},
|
|
|
|
stateStorageKey() {
|
|
|
|
return `attract.${this.projectId}.${this.contextType}`;
|
|
|
|
},
|
|
|
|
initialAppState() {
|
|
|
|
let stateJsonStr;
|
|
|
|
try {
|
|
|
|
stateJsonStr = localStorage.getItem(this.stateStorageKey);
|
|
|
|
} catch (error) {
|
|
|
|
// Log and ignore.
|
|
|
|
console.warn('Unable to restore state:', error);
|
|
|
|
}
|
|
|
|
return stateJsonStr ? JSON.parse(stateJsonStr) : undefined;
|
|
|
|
},
|
|
|
|
initialTableState() {
|
|
|
|
return this.initialAppState ? this.initialAppState.tableState : undefined;
|
2019-03-13 13:53:40 +01:00
|
|
|
}
|
|
|
|
},
|
|
|
|
watch: {
|
|
|
|
selectedItems(newValue) {
|
|
|
|
function equals(arrA, arrB) {
|
|
|
|
if (arrA.length === arrB.length) {
|
|
|
|
return arrA.every(it => arrB.includes(it)) &&
|
|
|
|
arrB.every(it => arrA.includes(it))
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
let newSelectedIds = newValue.map(item => item._id);
|
|
|
|
// They will be equal for instance when we pop browser history
|
|
|
|
if (equals(newSelectedIds, this.selectedIds)) return;
|
|
|
|
|
|
|
|
this.selectedIds = newSelectedIds;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
methods: {
|
|
|
|
onSelectItemsChanged(selectedItems) {
|
|
|
|
this.selectedItems = selectedItems;
|
|
|
|
},
|
|
|
|
onEditingObjects(isEditing) {
|
|
|
|
this.isEditing = !!isEditing;
|
|
|
|
},
|
|
|
|
onTableInitialized() {
|
|
|
|
this.isTableInited = true;
|
|
|
|
},
|
2019-03-28 10:29:13 +01:00
|
|
|
/**
|
|
|
|
* Save table state to localStorage per project and context
|
|
|
|
* @param {Object} newState
|
|
|
|
*/
|
|
|
|
onTableStateChanged(newState) {
|
|
|
|
let appState = new ComponentState(newState);
|
|
|
|
let stateJsonStr = JSON.stringify(appState);
|
|
|
|
try {
|
|
|
|
localStorage.setItem(this.stateStorageKey, stateJsonStr);
|
|
|
|
} catch (error) {
|
|
|
|
// Log and ignore.
|
|
|
|
console.warn('Unable to save state:', error);
|
|
|
|
}
|
|
|
|
},
|
2019-03-13 13:53:40 +01:00
|
|
|
canChangeSelectionCB() {
|
|
|
|
if(this.isEditing) {
|
|
|
|
let retval = confirm("You have unsaved data. Do you want to discard it?");
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
},
|
|
|
|
/**
|
|
|
|
* @override BrowserHistoryState
|
|
|
|
*/
|
|
|
|
stateSaveMode(newState, oldState) {
|
|
|
|
if (!this.isTableInited) {
|
|
|
|
return StateSaveMode.IGNORE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!oldState) {
|
|
|
|
// Initial state. Replace what we have so we can go back to this state
|
|
|
|
return StateSaveMode.REPLACE;
|
|
|
|
}
|
|
|
|
if (newState.selectedIds.length > 1 && oldState.selectedIds.length > 1) {
|
|
|
|
// To not spam history when multiselecting items
|
|
|
|
return StateSaveMode.REPLACE;
|
|
|
|
}
|
|
|
|
return StateSaveMode.PUSH;
|
|
|
|
},
|
|
|
|
/**
|
|
|
|
* @override BrowserHistoryState
|
|
|
|
*/
|
|
|
|
applyHistoryState(newState) {
|
|
|
|
this.selectedIds = newState.selectedIds || this.selectedIds;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
});
|