Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create shared assets folder #414

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
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
3 changes: 3 additions & 0 deletions src/components/dynamic-editor.vue
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,9 @@
:centerSlide="centerSlide"
:dynamicSelected="dynamicSelected"
@slide-edit="$emit('slide-edit')"
@shared-asset="(assetName: string, oppositeLang: string) => {
$emit('shared-asset', assetName, oppositeLang);
}"
></component>
</div>
</div>
Expand Down
61 changes: 58 additions & 3 deletions src/components/editor.vue
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,8 @@
:slideIndex="slideIndex"
@slide-change="selectSlide"
@slides-updated="updateSlides"
@shared-asset="onSharedAsset"
@process-panel="panelHelper"
:configFileStructure="configFileStructure"
:lang="configLang"
:sourceCounts="sourceCounts"
Expand Down Expand Up @@ -323,6 +325,8 @@
:slideIndex="slideIndex"
@slide-change="selectSlide"
@slides-updated="updateSlides"
@shared-asset="onSharedAsset"
@process-panel="panelHelper"
:configFileStructure="configFileStructure"
:lang="configLang"
:sourceCounts="sourceCounts"
Expand All @@ -342,6 +346,7 @@
:slideIndex="slideIndex"
:isLast="slideIndex === slides.length - 1"
:uid="uuid"
@shared-asset="onSharedAsset"
@slide-change="selectSlide"
@slide-edit="onSlidesEdited"
@custom-slide-updated="updateCustomSlide"
Expand Down Expand Up @@ -376,14 +381,17 @@
<script lang="ts">
import { Options, Prop, Vue, Watch } from 'vue-property-decorator';
import {
BasePanel,
ConfigFileStructure,
HelpSection,
ImagePanel,
MetadataContent,
MultiLanguageSlide,
Slide,
SourceCounts,
StoryRampConfig,
TextPanel
TextPanel,
VideoPanel
} from '@/definitions';
import { VueSpinnerOval } from 'vue3-spinners';
import axios from 'axios';
Expand Down Expand Up @@ -451,6 +459,51 @@ export default class EditorV extends Vue {
this.$emit('save-status', true);
}

/**
* Executes a callback for each ImagePanel/VideoPanel within the provided BasePanel
*
* @param panel The panel which we are processing
* @param callback The callback function that is called on each ImagePanel/VideoPanel in the provided BasePanel
* @param callbackArgs The additional argument(s) for the callback function (can be empty)
*/
panelHelper(panel: BasePanel, callback: (panel: ImagePanel | VideoPanel, ...args) => void, ...callbackArgs): void {
switch (panel.type) {
case 'slideshow':
panel.items.forEach((item) => this.panelHelper(item, callback, ...callbackArgs));
break;
case 'dynamic':
panel.children.forEach((child) => this.panelHelper(child.panel, callback, ...callbackArgs));
break;
case 'image':
case 'video':
callback(panel, ...callbackArgs);
}
}

// move asset from opposite lang's assets folder to shared assets folder
onSharedAsset(assetName: string, oppositeLang: string): void {
const oppositeConfig = this.configs[oppositeLang];

const updateAssetSrc = (panel: ImagePanel | VideoPanel, assetName: string, oppositeLang: string) => {
if (panel.src) {
let assetSrc = panel.src.split('/');
const assetFolder = assetSrc[2];
if (panel.src.includes(assetName) && assetFolder === oppositeLang) {
assetSrc[2] = 'shared';
panel.src = assetSrc.join('/');
}
}
};

oppositeConfig?.slides.forEach((slide) => {
slide.panel.forEach((panel) => {
this.panelHelper(panel, updateAssetSrc, assetName, oppositeLang);
});
});

this.$emit('save-status', true);
}

@Watch('metadata', { deep: true })
onMetadataEdited(): void {
this.$emit('save-status', true);
Expand Down Expand Up @@ -511,15 +564,17 @@ export default class EditorV extends Vue {
panel: [{ type: 'loading-page' }, { type: 'loading-page' }]
};

const newLang = lang ? lang : this.configLang ? this.configLang : 'en';
this.$emit('lang-change', newLang);

setTimeout(() => {
if (index === -1 || !this.loadSlides) {
this.currentSlide = '';
} else {
const selectedLang = (lang ?? this.configLang) as keyof MultiLanguageSlide;
const selectedLang = newLang as keyof MultiLanguageSlide;
const selectedSlide = this.loadSlides[index][selectedLang];
this.currentSlide = selectedSlide ?? '';
}

this.slideIndex = index;
(this.$refs.slide as SlideEditorV).panelIndex = 0;
(this.$refs.slide as SlideEditorV).advancedEditorView = false;
Expand Down
1 change: 1 addition & 0 deletions src/components/helpers/custom-editor.vue
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ export default class CustomEditorV extends Vue {
}
saveChanges(): void {
console.log('customeditor.vue - saving changes after opening advanced editor ');
this.$emit('config-edited', this.updatedConfig);
this.edited = false;
Expand Down
105 changes: 64 additions & 41 deletions src/components/image-editor.vue
Original file line number Diff line number Diff line change
Expand Up @@ -244,35 +244,60 @@ export default class ImageEditorV extends Vue {
this.imagePreviews = res;
this.imagePreviewsLoading = false;
});

this.slideshowCaption = this.panel.caption as string;
}
}

// Helper for onFileChange and dropImages. Maps a File object to an ImageFile object
addUploadedFile(file: File) {
let uploadSource = `${this.configFileStructure.uuid}/assets/shared/${file.name}`;
const oppositeLang = this.lang === 'en' ? 'fr' : 'en';
let oppositeSourceCount = 0;
const inOppositeAssets = !!this.configFileStructure.assets[oppositeLang].file(file.name);
const inSharedAssets = !!this.configFileStructure.assets['shared'].file(file.name);

// If the file is already in the opposite langs assets folder, we should move it to the shared
// assets folder, rather than duplicating it within the current lang's assets folder. Otherwise,
// if the file is already in the shared assets folder, then nothing needs to be done, since the
// panel config will simply access the file from the shared assets folder. If the asset is neither
// in the shared nor the opposite langs assets folder, the file should be uploaded to the current
// langs assets folder
if (inOppositeAssets) {
const oppositeFileSource = `${this.configFileStructure.uuid}/assets/${oppositeLang}/${file.name}`;
oppositeSourceCount = this.sourceCounts[oppositeFileSource] ?? 0;
this.sourceCounts[oppositeFileSource] = 0;
this.configFileStructure.assets[oppositeLang].remove(file.name);
this.configFileStructure.assets['shared'].file(file.name, file);
this.$emit('shared-asset', file.name, oppositeLang);
} else if (!inSharedAssets) {
uploadSource = `${this.configFileStructure.uuid}/assets/${this.lang}/${file.name}`;
this.configFileStructure.assets[this.lang].file(file.name, file);
}

if (this.sourceCounts[uploadSource]) {
this.sourceCounts[uploadSource] += 1 + oppositeSourceCount;
} else {
this.sourceCounts[uploadSource] = 1 + oppositeSourceCount;
}

let imageSrc = URL.createObjectURL(file);
return {
id: file.name,
altText: '',
caption: '',
src: imageSrc
};
}

onFileChange(e: Event): void {
// create object URL(s) to display image(s)
const filelist = Array.from((e.target as HTMLInputElement).files as ArrayLike<File>);
this.imagePreviews.push(
...filelist.map((file: File) => {
// Add the uploaded images to the product ZIP file.
const uploadSource = `${this.configFileStructure.uuid}/assets/${this.lang}/${file.name}`;
this.configFileStructure.assets[this.lang].file(file.name, file);

if (this.sourceCounts[uploadSource]) {
this.sourceCounts[uploadSource] += 1;
} else {
this.sourceCounts[uploadSource] = 1;
}

let imageSrc = URL.createObjectURL(file);
return {
id: file.name,
altText: '',
caption: '',
src: imageSrc
};
return this.addUploadedFile(file);
})
);

this.onImagesEdited();
}

Expand All @@ -287,23 +312,7 @@ export default class ImageEditorV extends Vue {

this.imagePreviews.push(
...files.map((file: File) => {
// Add the uploaded images to the product ZIP file.
const uploadSource = `${this.configFileStructure.uuid}/assets/${this.lang}/${file.name}`;
this.configFileStructure.assets[this.lang].file(file.name, file);

if (this.sourceCounts[uploadSource]) {
this.sourceCounts[uploadSource] += 1;
} else {
this.sourceCounts[uploadSource] = 1;
}

let imageSrc = URL.createObjectURL(file);
return {
id: file.name,
altText: '',
caption: '',
src: imageSrc
};
return this.addUploadedFile(file);
})
);
this.dragging = false;
Expand All @@ -314,12 +323,15 @@ export default class ImageEditorV extends Vue {
deleteImage(img: ImageFile): void {
const idx = this.imagePreviews.findIndex((file: ImageFile) => file.id === img.id);
if (idx !== -1) {
const fileSource = `${this.configFileStructure.uuid}/assets/${this.lang}/${this.imagePreviews[idx].id}`;
const assetLocation = this.srcFolder(this.imagePreviews[idx].id);

// Set file source based on whether it exists in the shared assets folder or not
const fileSource = `${this.configFileStructure.uuid}/assets/${assetLocation}/${this.imagePreviews[idx].id}`;

// Remove the image from the product ZIP file.
this.sourceCounts[fileSource] -= 1;
if (this.sourceCounts[fileSource] === 0) {
this.configFileStructure.assets[this.lang].remove(this.imagePreviews[idx].id);
this.configFileStructure.assets[assetLocation].remove(this.imagePreviews[idx].id);
URL.revokeObjectURL(this.imagePreviews[idx].src);
}
this.imagePreviews.splice(idx, 1);
Expand Down Expand Up @@ -355,18 +367,24 @@ export default class ImageEditorV extends Vue {
// @ts-ignore
(this.panel as ImagePanel)[key] = imageFile[key];
});

(this.panel as ImagePanel).src = `${this.configFileStructure.uuid}/assets/${this.lang}/${imageFile.id}`;
// Set the image source based on whether the image is in the shared assets folder or not
(this.panel as ImagePanel).src = `${this.configFileStructure.uuid}/assets/${this.srcFolder(
imageFile.id
)}/${imageFile.id}`;
} else {
// Otherwise, convert this to a slideshow panel.
this.panel.type = PanelType.Slideshow;
this.panel.caption = this.slideshowCaption ?? undefined;

// Turn each of the image configs into an image panel and add them to the slidesow.
// Turn each of the image configs into an image panel and add them to the slideshow.
(this.panel as SlideshowPanel).items = this.imagePreviews.map((imageFile: ImageFile) => {
// Set the image source based on whether the image is in the shared assets folder or not
const imageSource = `${this.configFileStructure.uuid}/assets/${this.srcFolder(imageFile.id)}/${
imageFile.id
}`;
return {
...imageFile,
src: `${this.configFileStructure.uuid}/assets/${this.lang}/${imageFile.id}`,
src: imageSource,
type: PanelType.Image
} as ImagePanel;
});
Expand All @@ -379,6 +397,11 @@ export default class ImageEditorV extends Vue {
this.edited = true;
this.$emit('slide-edit', this.imagePreviews.length !== 0);
}

// checks whether an image belongs to current lang's assets folder or the shared assets folder
srcFolder(imageId: string): string {
return this.configFileStructure.assets['shared'].file(imageId) ? 'shared' : this.lang;
}
}
</script>

Expand Down
15 changes: 11 additions & 4 deletions src/components/metadata-editor.vue
Original file line number Diff line number Diff line change
Expand Up @@ -485,6 +485,7 @@
@save-status="updateSaveStatus"
@refresh-config="refreshConfig"
@export-product="exportProduct"
@lang-change="changeLang"
ref="mainEditor"
>
<!-- Metadata editing modal inside the editor -->
Expand Down Expand Up @@ -724,7 +725,6 @@ export default class MetadataEditorV extends Vue {
this.metadata.tocOrientation = 'vertical';
this.metadata.returnTop = true;
}

// Find which view to render based on route
if (this.$route.name === 'editor') {
this.loadEditor = true;
Expand Down Expand Up @@ -796,6 +796,10 @@ export default class MetadataEditorV extends Vue {
}
}

changeLang(lang: string): void {
this.configLang = lang;
}

/**
* Open current editor config as a new Storylines product in new tab.
* Note: Preview button on metadata editor will only show when editing an existing product, not cwhen creating a new one
Expand Down Expand Up @@ -1279,7 +1283,8 @@ export default class MetadataEditorV extends Vue {
configs: this.configs as unknown as { [key: string]: StoryRampConfig },
assets: {
en: (assetsFolder as JSZip).folder('en') as JSZip,
fr: (assetsFolder as JSZip).folder('fr') as JSZip
fr: (assetsFolder as JSZip).folder('fr') as JSZip,
shared: (assetsFolder as JSZip).folder('shared') as JSZip
},
charts: {
en: (chartsFolder as JSZip).folder('en') as JSZip,
Expand All @@ -1289,6 +1294,8 @@ export default class MetadataEditorV extends Vue {
};

// If uploadLogo is set, upload the logo to the directory.
// Q: if we create a shared assets folder, should the logo just go there, since both langs
// should have the same logo?
if (uploadLogo !== undefined) {
this.configFileStructure.assets[this.configLang].file(uploadLogo?.name, uploadLogo);
}
Expand Down Expand Up @@ -1447,10 +1454,10 @@ export default class MetadataEditorV extends Vue {
const frFileName = `${this.uuid}_fr.json`;

// Replace undefined slides with empty objects
this.configs.en!.slides = this.configs.en!.slides.map((slide) => {
this.configs.en.slides = this.configs.en?.slides.map((slide) => {
return slide ?? {};
});
this.configs.fr!.slides = this.configs.fr!.slides.map((slide) => {
this.configs.fr.slides = this.configs.fr?.slides.map((slide) => {
return slide ?? {};
});
this.loadSlides(this.configs);
Expand Down
12 changes: 11 additions & 1 deletion src/components/slide-editor.vue
Original file line number Diff line number Diff line change
Expand Up @@ -333,7 +333,12 @@
ref="editor"
:config="currentSlide"
@slide-edit="$emit('slide-edit')"
@config-edited="(slideConfig: Slide, save?: boolean = false) => $emit('custom-slide-updated', slideConfig, save, lang)"
@config-edited="(slideConfig: Slide, save?: boolean = false) => {
$emit('custom-slide-updated', slideConfig, save, lang)
}"
@shared-asset="(assetName: string, oppositeLang: string) => {
$emit('shared-asset', assetName, oppositeLang);
}"
v-if="advancedEditorView"
></custom-editor>
<component
Expand All @@ -347,6 +352,9 @@
:sourceCounts="sourceCounts"
:centerSlide="centerSlide"
:dynamicSelected="dynamicSelected"
@shared-asset="(assetName: string, oppositeLang: string) => {
$emit('shared-asset', assetName, oppositeLang);
}"
@slide-edit="(changedFromDefault: boolean = true) => {
$emit('slide-edit');

Expand Down Expand Up @@ -596,6 +604,8 @@ export default class SlideEditorV extends Vue {
}

case 'image': {
// as long as assets are uploaded/removed from the appropriate folders (either shared or current lang),
// then this should work fine as is
const imagePanel = panel as ImagePanel;
this.sourceCounts[imagePanel.src] -= 1;
if (this.sourceCounts[imagePanel.src] === 0) {
Expand Down
Loading
Loading