Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions client/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion client/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "client",
"version": "0.27.2",
"version": "0.27.3",
"description": "DigiScript front end",
"author": "DreamTeamProd",
"private": true,
Expand Down
134 changes: 134 additions & 0 deletions client/src/vue_components/show/config/script/BulkActSceneModal.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
<template>
<b-modal
id="bulk-act-scene-modal"
title="Bulk Edit Act/Scene"
size="md"
ok-title="Apply"
:ok-disabled="!canApply"
@ok="handleOk"
@hidden="reset"
>
<p class="text-muted small mb-3">
Applies the selected act and scene to all lines from the chosen start line to end line
(inclusive).
</p>
<b-form-group label="Act" label-for="bulk-act-input">
<b-form-select
id="bulk-act-input"
v-model="selectedActId"
:options="actOptions"
@change="selectedSceneId = null"
/>
</b-form-group>
<b-form-group label="Scene" label-for="bulk-scene-input">
<b-form-select
id="bulk-scene-input"
v-model="selectedSceneId"
:options="sceneOptions"
:disabled="selectedActId == null"
/>
</b-form-group>
</b-modal>
</template>

<script>
export default {
name: 'BulkActSceneModal',
events: ['apply'],
props: {
previousLineOfStart: {
type: Object,
default: null,
},
nextLineOfEnd: {
type: Object,
default: null,
},
acts: {
required: true,
type: Array,
},
scenes: {
required: true,
type: Array,
},
},
data() {
return {
selectedActId: null,
selectedSceneId: null,
};
},
computed: {
validActs() {
// Same algorithm as nextActs in ScriptLineEditor — constrained by the line before
// the start selection (lower bound) and the line after the end selection (upper bound).
let startAct = this.acts.find((act) => act.previous_act == null);
if (this.previousLineOfStart != null && this.previousLineOfStart.act_id != null) {
startAct = this.acts.find((act) => act.id === this.previousLineOfStart.act_id);
}
const validActs = [];
let nextAct = startAct;
while (nextAct != null) {
validActs.push(nextAct);
if (this.nextLineOfEnd != null && this.nextLineOfEnd.act_id === nextAct.id) {
break;
}
nextAct = this.acts.find((act) => act.id === nextAct.next_act) || null;
}
return validActs;
},
validScenes() {
if (this.selectedActId == null) {
return [];
}
const actScenes = this.scenes.filter((scene) => scene.act === this.selectedActId);
let startScene = actScenes.find((scene) => scene.previous_scene == null);
if (
this.previousLineOfStart != null &&
this.previousLineOfStart.act_id === this.selectedActId &&
this.previousLineOfStart.scene_id != null
) {
startScene = actScenes.find((scene) => scene.id === this.previousLineOfStart.scene_id);
}
const validScenes = [];
let nextScene = startScene;
while (nextScene != null) {
validScenes.push(nextScene);
if (this.nextLineOfEnd != null && this.nextLineOfEnd.scene_id === nextScene.id) {
break;
}
nextScene = actScenes.find((scene) => scene.id === nextScene.next_scene) || null;
}
return validScenes;
},
actOptions() {
return [
{ value: null, text: 'Select an act', disabled: true },
...this.validActs.map((act) => ({ value: act.id, text: act.name })),
];
},
sceneOptions() {
return [
{ value: null, text: 'Select a scene', disabled: true },
...this.validScenes.map((scene) => ({ value: scene.id, text: scene.name })),
];
},
canApply() {
return this.selectedActId != null && this.selectedSceneId != null;
},
},
methods: {
handleOk(bvModalEvt) {
bvModalEvt.preventDefault();
if (this.canApply) {
this.$emit('apply', { actId: this.selectedActId, sceneId: this.selectedSceneId });
}
},
reset() {
this.selectedActId = null;
this.selectedSceneId = null;
},
},
};
</script>
135 changes: 134 additions & 1 deletion client/src/vue_components/show/config/script/ScriptEditor.vue
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,13 @@
>
Save
</b-button>
<b-button
v-if="canEdit && !IS_CUT_MODE"
:variant="bulkEditMode ? 'info' : 'outline-info'"
@click="bulkEditMode ? exitBulkEditMode() : enterBulkEditMode()"
>
{{ bulkEditMode ? 'Exit Bulk Edit' : 'Bulk Edit' }}
</b-button>
</b-button-group>
</b-col>
</b-row>
Expand Down Expand Up @@ -98,13 +105,18 @@
:line-part-cuts="linePartCuts"
:stage-direction-styles="STAGE_DIRECTION_STYLES"
:stage-direction-style-overrides="STAGE_DIRECTION_STYLE_OVERRIDES"
:bulk-edit-mode="bulkEditMode"
:is-bulk-start="isBulkStart(index)"
:is-bulk-end="isBulkEnd(index)"
@editLine="beginEditingLine(currentEditPage, index)"
@cutLinePart="cutLinePart"
@insertDialogue="insertDialogueAt(currentEditPage, index)"
@insertStageDirection="insertStageDirectionAt(currentEditPage, index)"
@insertCueLine="insertCueLineAt(currentEditPage, index)"
@insertSpacing="insertSpacingAt(currentEditPage, index)"
@deleteLine="deleteLine(currentEditPage, index)"
@set-bulk-start="onSetBulkStart(index)"
@set-bulk-end="onSetBulkEnd(index)"
/>
</template>
</template>
Expand Down Expand Up @@ -155,6 +167,13 @@
/>
</div>
</b-modal>
<bulk-act-scene-modal
:previous-line-of-start="previousLineOfStart"
:next-line-of-end="nextLineOfEnd"
:acts="ACT_LIST"
:scenes="SCENE_LIST"
@apply="onBulkApply"
/>
<b-modal
id="go-to-page-script-editor"
ref="go-to-page-script-editor"
Expand Down Expand Up @@ -201,6 +220,7 @@ import { diff } from 'deep-object-diff';
import log from 'loglevel';
import { sample } from 'lodash';

import BulkActSceneModal from '@/vue_components/show/config/script/BulkActSceneModal.vue';
import ScriptLineEditor from '@/vue_components/show/config/script/ScriptLineEditor.vue';
import ScriptLineViewer from '@/vue_components/show/config/script/ScriptLineViewer.vue';
import { makeURL, randInt } from '@/js/utils';
Expand All @@ -209,7 +229,7 @@ import { LINE_TYPES } from '@/constants/lineTypes';

export default {
name: 'ScriptConfig',
components: { ScriptLineViewer, ScriptLineEditor },
components: { BulkActSceneModal, ScriptLineViewer, ScriptLineEditor },
data() {
return {
currentEditPage: 1,
Expand Down Expand Up @@ -238,6 +258,11 @@ export default {
autoSaveInterval: null,
isAutoSaving: false,
navbarHeight: 0,
bulkEditMode: false,
bulkEditStart: null,
bulkEditEnd: null,
previousLineOfStart: null,
nextLineOfEnd: null,
};
},
validations: {
Expand Down Expand Up @@ -317,6 +342,12 @@ export default {
currentEditPage(val) {
localStorage.setItem('scriptEditPage', val);
},
async bulkEditEnd(val) {
if (val != null) {
await this.loadBoundaryLines();
this.$bvModal.show('bulk-act-scene-modal');
}
},
USER_SETTINGS() {
this.setupAutoSave();
},
Expand Down Expand Up @@ -412,6 +443,7 @@ export default {
}
}
this.editPages = [];
this.exitBulkEditMode();
this.RESET_TO_SAVED(this.currentEditPage);
this.resetCutsToSaved();
this.$socket.sendObj({
Expand Down Expand Up @@ -918,6 +950,107 @@ export default {
this.navbarHeight = 56;
}
},
enterBulkEditMode() {
this.bulkEditMode = true;
this.bulkEditStart = null;
this.bulkEditEnd = null;
},
exitBulkEditMode() {
this.bulkEditMode = false;
this.bulkEditStart = null;
this.bulkEditEnd = null;
this.previousLineOfStart = null;
this.nextLineOfEnd = null;
},
onSetBulkStart(index) {
this.bulkEditStart = {
page: this.currentEditPage,
lineIndex: index,
};
this.bulkEditEnd = null;
},
onSetBulkEnd(index) {
const { page: startPage, lineIndex: startIndex } = this.bulkEditStart || {};
if (startPage === this.currentEditPage && index <= startIndex) {
this.$toast.error('End line must come after start line');
return;
}
if (startPage != null && this.currentEditPage < startPage) {
this.$toast.error('End line must come after start line');
return;
}
this.bulkEditEnd = { page: this.currentEditPage, lineIndex: index };
},
isBulkStart(index) {
return (
this.bulkEditStart != null &&
this.bulkEditStart.page === this.currentEditPage &&
this.bulkEditStart.lineIndex === index
);
},
isBulkEnd(index) {
return (
this.bulkEditEnd != null &&
this.bulkEditEnd.page === this.currentEditPage &&
this.bulkEditEnd.lineIndex === index
);
},
async loadBoundaryLines() {
const { page: startPage, lineIndex: startIndex } = this.bulkEditStart;
const { page: endPage, lineIndex: endIndex } = this.bulkEditEnd;

if (startIndex === 0 && startPage > 1) {
await this.LOAD_SCRIPT_PAGE(startPage - 1);
}
const endPageLines = this.TMP_SCRIPT[endPage.toString()];
if (endPageLines && endIndex === endPageLines.length - 1) {
await this.LOAD_SCRIPT_PAGE(endPage + 1);
}

const startPageLines = this.TMP_SCRIPT[startPage.toString()];
if (startIndex > 0 && startPageLines) {
this.previousLineOfStart = startPageLines[startIndex - 1] || null;
} else if (startPage > 1) {
const prevPage = this.TMP_SCRIPT[(startPage - 1).toString()];
this.previousLineOfStart = prevPage ? prevPage[prevPage.length - 1] : null;
} else {
this.previousLineOfStart = null;
}

const endLines = this.TMP_SCRIPT[endPage.toString()];
if (endLines && endIndex < endLines.length - 1) {
this.nextLineOfEnd = endLines[endIndex + 1] || null;
} else {
const nextPage = this.TMP_SCRIPT[(endPage + 1).toString()];
this.nextLineOfEnd = nextPage ? nextPage[0] : null;
}
},
async onBulkApply({ actId, sceneId }) {
const { page: startPage, lineIndex: startIndex } = this.bulkEditStart;
const { page: endPage, lineIndex: endIndex } = this.bulkEditEnd;

for (let p = startPage; p <= endPage; p++) {
await this.LOAD_SCRIPT_PAGE(p);
}

for (let p = startPage; p <= endPage; p++) {
const pageLines = this.TMP_SCRIPT[p.toString()];
if (!pageLines) continue;
const fromIndex = p === startPage ? startIndex : 0;
const toIndex = p === endPage ? endIndex : pageLines.length - 1;
for (let i = fromIndex; i <= toIndex; i++) {
if (this.DELETED_LINES(p).includes(i)) continue;
this.SET_LINE({
pageNo: p,
lineIndex: i,
lineObj: { ...pageLines[i], act_id: actId, scene_id: sceneId },
});
}
}

this.$bvModal.hide('bulk-act-scene-modal');
this.exitBulkEditMode();
},
...mapMutations([
'REMOVE_PAGE',
'ADD_BLANK_LINE',
Expand Down
Loading
Loading