From 8892beccbceab08bc2c03de0d1b7c9c7ecfd2a1f Mon Sep 17 00:00:00 2001 From: peterpeterparker Date: Sun, 31 Jan 2021 14:14:43 +0100 Subject: [PATCH 1/7] feat: publish templates to api --- cloud/functions/src/assets/templates.json | 44 +++++++++++++++++ cloud/functions/src/model/api/api.slide.ts | 2 +- cloud/functions/src/model/data/slide.ts | 46 ++++-------------- cloud/functions/src/model/data/template.ts | 37 ++++++++++++++ .../src/request/utils/convert-deck-utils.ts | 48 ++++++++++++++----- .../src/utils/data/template-utils.ts | 47 ++++++++++++++++++ cloud/functions/tsconfig.json | 7 ++- 7 files changed, 177 insertions(+), 54 deletions(-) create mode 100644 cloud/functions/src/assets/templates.json create mode 100644 cloud/functions/src/model/data/template.ts create mode 100644 cloud/functions/src/utils/data/template-utils.ts 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..d1244f3e8 100644 --- a/cloud/functions/src/request/utils/convert-deck-utils.ts +++ b/cloud/functions/src/request/utils/convert-deck-utils.ts @@ -4,14 +4,30 @@ 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 cdns: string[] = apiSlides + .filter( + (slideAndTemplate: SlideAndTemplate) => slideAndTemplate.template !== undefined && (slideAndTemplate.template as Template).data.cdn !== undefined + ) + .map((slideAndTemplate: SlideAndTemplate) => (slideAndTemplate.template as Template).data.cdn as string); + + // TODO: pass CDN to API + console.log('CDN', cdns, [...new Set(cdns)]); const apiDeck: ApiDeck = { name: deck.data.name ? deck.data.name.trim() : deck.data.name, @@ -23,7 +39,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); @@ -73,20 +89,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 +112,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 +121,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 +132,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 +144,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, + }); }); } 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..cbe0a0d2a --- /dev/null +++ b/cloud/functions/src/utils/data/template-utils.ts @@ -0,0 +1,47 @@ +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