diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4fced1aa9..35bc9cc5e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -19,7 +19,7 @@
### Others
- cli: v2.1.1 ([CHANGELOG](https://github.com/deckgo/deckdeckgo/blob/master/cli/CHANGELOG.md))
-- cloud: v2.0.0 ([CHANGELOG](https://github.com/deckgo/deckdeckgo/blob/master/cloud/CHANGELOG.md))
+- cloud: v2.1.0 ([CHANGELOG](https://github.com/deckgo/deckdeckgo/blob/master/cloud/CHANGELOG.md))
- deck-utils: v3.3.1 ([CHANGELOG](https://github.com/deckgo/deckdeckgo/blob/master/utils/deck/CHANGELOG.md))
- kit: v2.0.3 ([CHANGELOG](https://github.com/deckgo/deckdeckgo/blob/master/kit/CHANGELOG.md))
- starter kit: v7.1.4 ([CHANGELOG](https://github.com/deckgo/starter-kit/blob/master/CHANGELOG.md))
diff --git a/cloud/CHANGELOG.md b/cloud/CHANGELOG.md
index c8f898e99..4a62b71de 100644
--- a/cloud/CHANGELOG.md
+++ b/cloud/CHANGELOG.md
@@ -1,3 +1,9 @@
+
+
+# 2.1.0 (2021-01-31)
+
+- publish templates to API
+
# 2.0.0 (2021-01-29)
diff --git a/cloud/functions/.gitignore b/cloud/functions/.gitignore
index 7fbb8b408..6aaa5be4a 100644
--- a/cloud/functions/.gitignore
+++ b/cloud/functions/.gitignore
@@ -1,8 +1,7 @@
## Compiled JavaScript files
-**/*.js
-**/*.js.map
+lib/
# Typescript v1 declaration files
typings/
-node_modules/
\ No newline at end of file
+node_modules/
diff --git a/cloud/functions/package-lock.json b/cloud/functions/package-lock.json
index ceb673333..bec4b3a9e 100644
--- a/cloud/functions/package-lock.json
+++ b/cloud/functions/package-lock.json
@@ -1,6 +1,6 @@
{
"name": "functions",
- "version": "2.0.0",
+ "version": "2.1.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
diff --git a/cloud/functions/package.json b/cloud/functions/package.json
index 2f15dd110..76b346238 100644
--- a/cloud/functions/package.json
+++ b/cloud/functions/package.json
@@ -1,7 +1,8 @@
{
"name": "functions",
- "version": "2.0.0",
+ "version": "2.1.0",
"scripts": {
+ "prebuild": "node ./scripts/copy-templates.js",
"lint": "tslint --project tsconfig.json",
"build": "tsc",
"serve": "npm run build && firebase serve --only functions",
diff --git a/cloud/functions/scripts/copy-templates.js b/cloud/functions/scripts/copy-templates.js
new file mode 100644
index 000000000..e1355276f
--- /dev/null
+++ b/cloud/functions/scripts/copy-templates.js
@@ -0,0 +1,5 @@
+#!/usr/bin/env node
+
+const fs = require('fs');
+
+fs.copyFileSync('../../studio/src/assets/templates.json', './src/assets/templates.json');
diff --git a/cloud/functions/src/assets/templates.json b/cloud/functions/src/assets/templates.json
new file mode 100644
index 000000000..a65036680
--- /dev/null
+++ b/cloud/functions/src/assets/templates.json
@@ -0,0 +1,44 @@
+{
+ "community": [
+ {
+ "id": "db8abf75-5372-41be-8ee6-799b4884882c",
+ "data": {
+ "tag": "deckgo-slide-countdown",
+ "author": {
+ "name": "Gaurav Mahto",
+ "url": "https://github.com/gauravmahto"
+ },
+ "cdn": "https://unpkg.com/@deckdeckgo/slide-countdown@latest/dist/deckdeckgo-slide-countdown/deckdeckgo-slide-countdown.esm.js",
+ "props": [
+ {
+ "name": "days",
+ "type": "number",
+ "placeholder": "The amount of days before your presentations"
+ },
+ {
+ "name": "hours",
+ "type": "number",
+ "placeholder": "The amount of hours before your presentations (max. 23)"
+ },
+ {
+ "name": "minutes",
+ "type": "number",
+ "placeholder": "The amount of minutes before your presentations (max. 59)"
+ },
+ {
+ "name": "until",
+ "type": "string",
+ "placeholder": "A specific date and time until when your presentation will start (format: 2021-08-27T23:25:59.000+02:00)"
+ }
+ ],
+ "slots": [
+ {
+ "name": "title",
+ "placeholder": "Display the remaining time until your presentation",
+ "types": ["h1", "h2", "h3", "section"]
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/cloud/functions/src/model/api/api.slide.ts b/cloud/functions/src/model/api/api.slide.ts
index 8172dcb34..cc49d31da 100644
--- a/cloud/functions/src/model/api/api.slide.ts
+++ b/cloud/functions/src/model/api/api.slide.ts
@@ -2,6 +2,6 @@ import {SlideAttributes, SlideTemplate} from '../data/slide';
export interface ApiSlide {
content?: string;
- template: SlideTemplate;
+ template: SlideTemplate | string;
attributes?: SlideAttributes;
}
diff --git a/cloud/functions/src/model/data/slide.ts b/cloud/functions/src/model/data/slide.ts
index b5e642a05..dfbd40916 100644
--- a/cloud/functions/src/model/data/slide.ts
+++ b/cloud/functions/src/model/data/slide.ts
@@ -14,52 +14,22 @@ export enum SlideTemplate {
PLAYGROUND = 'playground',
}
-export enum SlideChartType {
- LINE = 'line',
- PIE = 'pie',
- BAR = 'bar',
-}
-
-export enum SlideSplitType {
+export enum SlideScope {
DEFAULT = 'default',
- DEMO = 'demo',
+ COMMUNITY = 'community',
+ USER = 'user',
}
-export type SlideAttributesYAxisDomain = 'max' | 'extent';
-
export interface SlideAttributes {
- style?: string;
- src?: string;
- customBackground?: string;
- imgSrc?: string;
- imgAlt?: string;
-
- content?: string;
- customQRCode?: boolean;
-
- type?: SlideChartType | SlideSplitType;
- innerRadius?: number;
- animation?: boolean;
- datePattern?: string;
- yAxisDomain?: SlideAttributesYAxisDomain;
- smooth?: boolean;
- area?: boolean;
- ticks?: number;
- grid?: boolean;
- separator?: string;
-
- vertical?: boolean;
-
- imgMode?: string;
-
- customLoader?: boolean;
-
- theme?: string;
+ [key: string]: string | number | boolean;
}
export interface SlideData {
content?: string;
- template: SlideTemplate;
+
+ template: SlideTemplate | string;
+ scope?: SlideScope;
+
attributes?: SlideAttributes;
api_id?: string;
diff --git a/cloud/functions/src/model/data/template.ts b/cloud/functions/src/model/data/template.ts
new file mode 100644
index 000000000..b45c6c957
--- /dev/null
+++ b/cloud/functions/src/model/data/template.ts
@@ -0,0 +1,37 @@
+import {firestore} from 'firebase-admin';
+
+export interface TemplateDataSlot {
+ name: string;
+ placeholder?: string;
+ types?: string[];
+}
+
+export interface TemplateDataProp {
+ name: string;
+ type: 'string' | 'number' | 'boolean';
+ placeholder?: string;
+}
+
+export interface TemplateDataAuthor {
+ name: string;
+ url?: string;
+}
+
+export interface TemplateData {
+ owner_id: string;
+
+ tag: string;
+ cdn?: string;
+ author?: TemplateDataAuthor;
+ slots?: TemplateDataSlot[];
+ props?: TemplateDataProp[];
+
+ created_at?: firestore.Timestamp;
+ updated_at?: firestore.Timestamp;
+}
+
+export interface Template {
+ id: string;
+ ref: firestore.DocumentReference;
+ data: TemplateData;
+}
diff --git a/cloud/functions/src/request/utils/convert-deck-utils.ts b/cloud/functions/src/request/utils/convert-deck-utils.ts
index d03ad9b0a..5f141a82c 100644
--- a/cloud/functions/src/request/utils/convert-deck-utils.ts
+++ b/cloud/functions/src/request/utils/convert-deck-utils.ts
@@ -4,14 +4,21 @@ import {Deck} from '../../model/data/deck';
import {ApiDeck, ApiDeckAttributes} from '../../model/api/api.deck';
import {ApiSlide} from '../../model/api/api.slide';
import {Slide, SlideAttributes, SlideTemplate} from '../../model/data/slide';
+import {Template} from '../../model/data/template';
import {findSlide} from '../../utils/data/slide-utils';
import {getGoogleFontScript} from './google-fonts-utils';
+import {findTemplates, getTemplate} from '../../utils/data/template-utils';
+
+interface SlideAndTemplate {
+ apiSlide: ApiSlide;
+ template: Template | undefined;
+}
export function convertDeck(deck: Deck): Promise {
return new Promise(async (resolve, reject) => {
try {
- const apiSlides: ApiSlide[] = await convertSlides(deck);
+ const apiSlides: SlideAndTemplate[] = await convertSlides(deck);
const apiDeck: ApiDeck = {
name: deck.data.name ? deck.data.name.trim() : deck.data.name,
@@ -23,7 +30,7 @@ export function convertDeck(deck: Deck): Promise {
background: deck.data.background,
header: deck.data.header,
footer: deck.data.footer,
- slides: apiSlides,
+ slides: apiSlides.map((slideAndTemplate: SlideAndTemplate) => slideAndTemplate.apiSlide),
};
const googleFontScript: string | undefined = await getGoogleFontScript(deck);
@@ -31,6 +38,12 @@ export function convertDeck(deck: Deck): Promise {
apiDeck.head_extra = googleFontScript;
}
+ const scripts: string | undefined = getTemplateScripts(apiSlides);
+
+ if (scripts !== undefined) {
+ apiDeck.head_extra = apiDeck.head_extra !== undefined ? `${apiDeck.head_extra}${scripts}` : scripts;
+ }
+
const attributes: ApiDeckAttributes | undefined = await convertDeckAttributes(deck);
if (attributes !== undefined) {
apiDeck.attributes = attributes;
@@ -73,20 +86,22 @@ async function convertDeckAttributes(deck: Deck): Promise {
- return new Promise(async (resolve, reject) => {
+function convertSlides(deck: Deck): Promise {
+ return new Promise(async (resolve, reject) => {
if (!deck.data.slides || deck.data.slides.length <= 0) {
resolve([]);
return;
}
try {
- const promises: Promise[] = [];
+ const templates: Template[] = await findTemplates(deck.data.owner_id);
+
+ const promises: Promise[] = [];
for (let i: number = 0; i < deck.data.slides.length; i++) {
const slideId: string = deck.data.slides[i];
- promises.push(convertSlide(deck, slideId));
+ promises.push(convertSlide(deck, templates, slideId));
}
if (!promises || promises.length <= 0) {
@@ -94,7 +109,7 @@ function convertSlides(deck: Deck): Promise {
return;
}
- const slides: ApiSlide[] = await Promise.all(promises);
+ const slides: SlideAndTemplate[] = await Promise.all(promises);
resolve(slides);
} catch (err) {
@@ -103,8 +118,8 @@ function convertSlides(deck: Deck): Promise {
});
}
-function convertSlide(deck: Deck, slideId: string): Promise {
- return new Promise(async (resolve, reject) => {
+function convertSlide(deck: Deck, templates: Template[], slideId: string): Promise {
+ return new Promise(async (resolve, reject) => {
const slide: Slide = await findSlide(deck.id, slideId);
if (!slide || !slide.data) {
@@ -114,8 +129,11 @@ function convertSlide(deck: Deck, slideId: string): Promise {
const attributes: SlideAttributes | undefined = await convertAttributesToString(slide.data.attributes);
+ const slideTemplate: SlideTemplate | undefined = SlideTemplate[slide.data.template.toUpperCase() as keyof typeof SlideTemplate];
+ const slideTag: string = slideTemplate ? `deckgo-slide-${slideTemplate.toLowerCase()}` : slide.data.template;
+
const apiSlide: ApiSlide = {
- template: slide.data.template,
+ template: slideTag,
content: slide.data.content,
attributes: attributes,
};
@@ -123,7 +141,12 @@ function convertSlide(deck: Deck, slideId: string): Promise {
const cleanApiSlide: ApiSlide = await convertSlideQRCode(apiSlide);
cleanApiSlide.content = await cleanNotes(apiSlide.content);
- resolve(cleanApiSlide);
+ const template: Template | undefined = await getTemplate(templates, slide.data.scope, slide.data.template);
+
+ resolve({
+ apiSlide: cleanApiSlide,
+ template,
+ });
});
}
@@ -197,3 +220,16 @@ function convertSlideQRCode(apiSlide: ApiSlide): Promise {
resolve(apiSlide);
});
}
+
+function getTemplateScripts(apiSlides: SlideAndTemplate[]) {
+ const cdns: string[] | undefined = apiSlides
+ .filter((slideAndTemplate: SlideAndTemplate) => slideAndTemplate.template !== undefined && slideAndTemplate.template.data.cdn !== undefined)
+ .map((slideAndTemplate: SlideAndTemplate) => (slideAndTemplate.template as Template).data.cdn as string);
+
+ if (cdns !== undefined && cdns.length > 0) {
+ const uniqueCdns: string[] = [...new Set(cdns)];
+ return uniqueCdns.map((cdn: string) => ``).join();
+ }
+
+ return undefined;
+}
diff --git a/cloud/functions/src/utils/data/template-utils.ts b/cloud/functions/src/utils/data/template-utils.ts
new file mode 100644
index 000000000..792bc9523
--- /dev/null
+++ b/cloud/functions/src/utils/data/template-utils.ts
@@ -0,0 +1,44 @@
+import * as admin from 'firebase-admin';
+
+import {Template} from '../../model/data/template';
+import {SlideScope} from '../../model/data/slide';
+
+import * as communityTemplates from '../../assets/templates.json';
+
+export function findTemplates(ownerId: string): Promise {
+ return new Promise(async (resolve, reject) => {
+ try {
+ const snapshot: admin.firestore.QuerySnapshot = await admin.firestore().collection('templates').where('owner_id', '==', ownerId).get();
+
+ if (!snapshot || !snapshot.docs) {
+ resolve([]);
+ return;
+ }
+
+ const templates: Template[] = snapshot.docs.map((doc) => {
+ const id = doc.id;
+ const ref = doc.ref;
+
+ return {
+ id: id,
+ ref: ref,
+ data: doc.data(),
+ } as Template;
+ });
+
+ resolve(templates);
+ } catch (err) {
+ reject(err);
+ }
+ });
+}
+
+export async function getTemplate(userTemplates: Template[], scope: SlideScope | undefined, template: string): Promise {
+ if (!scope || scope === SlideScope.DEFAULT) {
+ return undefined;
+ }
+
+ const templates: Template[] = scope === SlideScope.COMMUNITY ? (communityTemplates.community as Template[]) : userTemplates;
+
+ return templates.find((filteredTemplate: Template) => filteredTemplate.data.tag === template);
+}
diff --git a/cloud/functions/tsconfig.json b/cloud/functions/tsconfig.json
index 7ce05d039..770b86870 100644
--- a/cloud/functions/tsconfig.json
+++ b/cloud/functions/tsconfig.json
@@ -6,10 +6,9 @@
"outDir": "lib",
"sourceMap": true,
"strict": true,
- "target": "es2017"
+ "target": "es2017",
+ "resolveJsonModule": true
},
"compileOnSave": true,
- "include": [
- "src"
- ]
+ "include": ["src"]
}
diff --git a/studio/src/app/utils/core/utils.ts b/studio/src/app/utils/core/utils.ts
index f077a4265..559714373 100644
--- a/studio/src/app/utils/core/utils.ts
+++ b/studio/src/app/utils/core/utils.ts
@@ -9,24 +9,20 @@ export interface InjectScript {
export class Utils {
static injectJS(scr: InjectScript): Promise {
return new Promise((resolve, reject) => {
- if (!document) {
- resolve();
- return;
- }
-
- if (document.getElementById(scr.id)) {
+ if (document?.getElementById(scr.id)) {
resolve('JS already loaded.');
return;
}
const script = document.createElement('script');
script.id = scr.id;
- script.async = true;
- script.defer = true;
script.src = scr.src;
if (scr.module) {
script.type = 'module';
+ } else {
+ script.async = true;
+ script.defer = true;
}
script.addEventListener('load', () => resolve('JS loaded.'), {once: true});
@@ -40,12 +36,7 @@ export class Utils {
static injectCSS(id: string, src: string): Promise {
return new Promise((resolve, reject) => {
- if (!document) {
- resolve();
- return;
- }
-
- if (document.getElementById(id)) {
+ if (document?.getElementById(id)) {
resolve('CSS already loaded.');
return;
}