@progressive-development/pd-spa-helper 0.0.1 → 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/dist/src/InitApplicationData.d.ts +3 -0
  2. package/dist/src/InitApplicationData.js +8 -0
  3. package/dist/src/InitApplicationData.js.map +1 -0
  4. package/dist/src/PdSpaHelper.d.ts +111 -6
  5. package/dist/src/PdSpaHelper.js +410 -12
  6. package/dist/src/PdSpaHelper.js.map +1 -1
  7. package/dist/src/defaultpage/default-login.d.ts +5 -0
  8. package/dist/src/defaultpage/default-login.js +16 -0
  9. package/dist/src/defaultpage/default-login.js.map +1 -0
  10. package/dist/src/firebase/auth.d.ts +4 -0
  11. package/dist/src/firebase/auth.js +28 -0
  12. package/dist/src/firebase/auth.js.map +1 -0
  13. package/dist/src/firebase/firestore-client.d.ts +9 -0
  14. package/dist/src/firebase/firestore-client.js +19 -0
  15. package/dist/src/firebase/firestore-client.js.map +1 -0
  16. package/dist/src/firebase/functions-client.d.ts +31 -0
  17. package/dist/src/firebase/functions-client.js +70 -0
  18. package/dist/src/firebase/functions-client.js.map +1 -0
  19. package/dist/src/index.d.ts +1 -1
  20. package/dist/src/index.js +1 -1
  21. package/dist/src/index.js.map +1 -1
  22. package/dist/src/pd-spa-helper.d.ts +0 -1
  23. package/dist/src/pd-spa-helper.js +4 -2
  24. package/dist/src/pd-spa-helper.js.map +1 -1
  25. package/dist/src/router/AppMain.d.ts +6 -0
  26. package/dist/src/router/AppMain.js +14 -0
  27. package/dist/src/router/AppMain.js.map +1 -0
  28. package/dist/src/service-call-controller2.d.ts +16 -0
  29. package/dist/src/service-call-controller2.js +43 -0
  30. package/dist/src/service-call-controller2.js.map +1 -0
  31. package/dist/src/tmpown/pd-login.d.ts +14 -0
  32. package/dist/src/tmpown/pd-login.js +118 -0
  33. package/dist/src/tmpown/pd-login.js.map +1 -0
  34. package/dist/src/tmpown/pd-panel-viewer.d.ts +18 -0
  35. package/dist/src/tmpown/pd-panel-viewer.js +187 -0
  36. package/dist/src/tmpown/pd-panel-viewer.js.map +1 -0
  37. package/dist/src/tmpown/pd-panel.d.ts +5 -0
  38. package/dist/src/tmpown/pd-panel.js +41 -0
  39. package/dist/src/tmpown/pd-panel.js.map +1 -0
  40. package/dist/src/tmpown/pd-toast.d.ts +12 -0
  41. package/dist/src/tmpown/pd-toast.js +114 -0
  42. package/dist/src/tmpown/pd-toast.js.map +1 -0
  43. package/dist/test/pd-spa-helper.test.js +2 -2
  44. package/dist/test/pd-spa-helper.test.js.map +1 -1
  45. package/dist/tsconfig.tsbuildinfo +1 -1
  46. package/package.json +7 -1
  47. package/pd-spa-helper.iml +9 -0
  48. package/src/InitApplicationData.ts +9 -0
  49. package/src/PdSpaHelper.ts +490 -16
  50. package/src/defaultpage/default-login.ts +15 -0
  51. package/src/firebase/auth.ts +30 -0
  52. package/src/firebase/firestore-client.ts +21 -0
  53. package/src/firebase/functions-client.ts +103 -0
  54. package/src/index.ts +1 -1
  55. package/src/pd-spa-helper.ts +3 -3
  56. package/src/router/AppMain.ts +10 -0
  57. package/src/service-call-controller2.ts +67 -0
  58. package/src/tmpown/pd-login.ts +126 -0
  59. package/src/tmpown/pd-panel-viewer.ts +193 -0
  60. package/src/tmpown/pd-panel.ts +43 -0
  61. package/src/tmpown/pd-toast.ts +114 -0
  62. package/test/pd-spa-helper.test.ts +2 -2
@@ -1,33 +1,507 @@
1
- import { initializeApp } from 'firebase/app';
2
- import { getAuth, onAuthStateChanged } from 'firebase/auth';
1
+ import { FirebaseApp, FirebaseOptions, initializeApp } from 'firebase/app';
2
+ import { getAuth, onAuthStateChanged, User } from 'firebase/auth';
3
+ // TODO: Add SDKs for Firebase products that you want to use
4
+ // https://firebase.google.com/docs/web/setup#available-libraries
3
5
 
4
6
  import { router, navigator } from 'lit-element-router';
5
7
 
6
- import { html, css, LitElement } from 'lit';
8
+ import { html, LitElement, css, CSSResultGroup, TemplateResult } from 'lit';
7
9
  import { property } from 'lit/decorators.js';
8
10
 
9
- export class PdSpaHelper extends router(navigator(LitElement)) {
11
+ import { initApplicationServices } from './InitApplicationData.js';
12
+
13
+ import { isAuthenticated } from './firebase/auth.js';
14
+ import { ServiceCallController } from './service-call-controller2.js';
15
+ import { callFunction, FunctionDefinition, FunctionResult, FunctionsConfig } from './firebase/functions-client.js';
16
+
17
+ import '@progressive-development/pd-page/pd-menu.js';
18
+ import '@progressive-development/pd-page/pd-footer.js';
19
+
20
+ import './router/AppMain.js';
21
+ import './defaultpage/default-login.js';
22
+ import './tmpown/pd-panel-viewer.js';
23
+ import './tmpown/pd-panel.js';
24
+
25
+ // TODO: How to solve this import problem?
26
+ // eslint-disable-next-line import/no-duplicates
27
+ import './tmpown/pd-toast.js';
28
+ // eslint-disable-next-line import/no-duplicates
29
+ import { PdToast } from './tmpown/pd-toast.js';
30
+
31
+ // Footer Text
32
+ const MADE_BY = {
33
+ txt: "made by PD Progressive Development",
34
+ email: "info@progressive-development.com"};
35
+
36
+ // Visible time for time-limited toast
37
+ const TOAST_DURATION = 6000;
38
+
39
+ /**
40
+ * Data models.
41
+ */
42
+
43
+ export interface MenuElement {
44
+ key: string,
45
+ name: string,
46
+ sec?: string,
47
+ route?: string,
48
+ action?: Function,
49
+ topItem?: boolean,
50
+ ref?: any,
51
+ icon?: string,
52
+ }
53
+
54
+ export interface NavigationPage {
55
+ name: string,
56
+ pattern: Array<string>,
57
+ auth: boolean,
58
+ menu?: Array<MenuElement>,
59
+ withTeaser?: boolean,
60
+ withFooter?: boolean
61
+ }
62
+
63
+ export interface NavigationConfig {
64
+ pages: Array<NavigationPage>,
65
+ includeLogin: boolean
66
+ }
67
+
68
+ /**
69
+ * Transformed routes, generated during startInit.
70
+ */
71
+ let transformedRoutes:Array<any>;
72
+ let navigationConfig:NavigationConfig;
73
+ let postLoginFunc:FunctionDefinition | undefined;
74
+
75
+ /**
76
+ * Internal helper functions to generate route elemets for lit-router.
77
+ */
78
+ const transformRoutes = () => {
79
+
80
+ const transformAuthConfig = (withAuth:boolean) =>
81
+ withAuth ?
82
+ {
83
+ unauthenticated: {
84
+ name: 'login',
85
+ },
86
+ authenticate: () => isAuthenticated(),
87
+ } : undefined;
88
+
89
+ let generatedRoutes:Array<any> = [];
90
+ navigationConfig.pages.forEach(cfEntry => {
91
+ const addAraay = cfEntry.pattern.map(routeMapPattern => (
92
+ {
93
+ name: cfEntry.name,
94
+ pattern: routeMapPattern,
95
+ authentication: transformAuthConfig(cfEntry.auth),
96
+ }
97
+ ));
98
+ generatedRoutes = generatedRoutes.concat(addAraay);
99
+ });
100
+
101
+ // add not-found route
102
+ generatedRoutes.push({
103
+ name: 'not-found',
104
+ pattern: '*',
105
+ });
106
+
107
+ return generatedRoutes;
108
+ }
109
+
110
+
111
+ /**
112
+ * Init the firebase app and application services like
113
+ * functions and firestore. Generate routes from navigationConfig.
114
+ *
115
+ * @param firebaseConfig
116
+ * @param functionsConfig
117
+ * @param navigationConfig
118
+ */
119
+ export const startInit = (firebaseConfig: FirebaseOptions,
120
+ functionsConfig: FunctionsConfig, navigationConfigParam: NavigationConfig) => {
121
+
122
+ // init firebase app
123
+ const app:FirebaseApp = initializeApp(firebaseConfig);
124
+
125
+ // init application services (functions, store)
126
+ initApplicationServices(app, functionsConfig);
127
+ postLoginFunc = functionsConfig.postLoginFunc;
128
+
129
+ if (navigationConfigParam.includeLogin) {
130
+ navigationConfigParam.pages.push({
131
+ name: "login",
132
+ menu: [
133
+ {key: "login", name: "Login"}
134
+ ],
135
+ pattern: ["login"],
136
+ auth: false
137
+ });
138
+ }
139
+
140
+ // generate routes from navigation config
141
+ navigationConfig = navigationConfigParam;
142
+ transformedRoutes = transformRoutes();
143
+ };
144
+
145
+ /**
146
+ * Abstract class for SPAs. Extend within the app main class.
147
+ */
148
+ export abstract class PdSpaHelper extends router(navigator(LitElement)) {
149
+
150
+ protected functionsController = new ServiceCallController(this);
151
+
152
+ // not used at the moment
153
+ @property({ type: String }) title = 'Hey there';
154
+
155
+ /**
156
+ * Properties needed for login/profile data.
157
+ */
158
+ @property({ type: Object, state: true }) _user: User | undefined;
159
+
160
+ @property({ type: Object, state: true }) _profile: any | undefined;
161
+
162
+ /**
163
+ * Properties needed for the router.
164
+ */
165
+ @property({ type: String, reflect: true }) route = "";
166
+
167
+ @property({ type: Object }) params = {};
168
+
169
+ @property({ type: Object }) query = {};
170
+
171
+ /**
172
+ * Indicates if the teaser should closed (depends on scroll position).
173
+ */
174
+ @property({ type: Boolean }) _teaserClosed = false;
175
+
176
+ static styles =
177
+ // Ref: Additional use classmap to add custom classes in concrete impl
178
+ css`
10
179
 
11
- static styles = css`
12
180
  :host {
181
+ display: flex;
182
+ flex-flow: column;
183
+ height: 100%;
184
+ }
185
+
186
+ header {
187
+ flex-grow: 0;
188
+ }
189
+
190
+ main {
191
+ flex-grow: 1;
192
+ flex-basis: max-content;
193
+ }
194
+
195
+ footer {
196
+ flex-grow: 0;
197
+ }
198
+
199
+ .default-header {
200
+ width: 100%;
201
+ position: fixed;
202
+ top: 0;
13
203
  display: block;
14
- padding: 25px;
15
- color: var(--pd-spa-helper-text-color, #000);
204
+ z-index: 99;
16
205
  }
17
- `;
18
206
 
19
- @property({ type: String }) title = 'Hey there';
207
+ .default-teaser {
208
+ --pd-panel-overflow: hidden;
209
+ --pd-panel-height: 32vh;
210
+ --pd-panel-width: 100%;
211
+ --pd-panel-border-radius: 0;
212
+ --pd-panel-bg: var(--pd-default-col);
213
+ --pd-panel-viewer-bg-col: var(--pd-default-col);
214
+ }
215
+
216
+ .default-menu {
217
+ transition: background-color 1s;
218
+ }
219
+
220
+ .default-menu-withteaser {
221
+ --pd-menu-bg-col: #AFC1D2;
222
+ --pd-menu-font-col: #0A3A48;
223
+ transition: background-color 1s;
224
+ }
225
+
226
+ .default-main {
227
+ padding-top: calc(var(--pd-menu-height) + 2em);
228
+ transition: padding-top 1s;
229
+ }
230
+
231
+ .default-main-withteaser {
232
+ padding-top: calc(var(--pd-menu-height) + 32vh);
233
+ transition: padding-top 1s;
234
+ }
235
+
236
+ ` as CSSResultGroup;
20
237
 
21
- @property({ type: Number }) counter = 5;
238
+ constructor() {
22
239
 
23
- __increment() {
24
- this.counter += 1;
240
+ super();
241
+
242
+ const doSomething = (scrollPos:Number) => {
243
+ if (scrollPos <= 50) {
244
+ this._teaserClosed = false;
245
+ } else {
246
+ this._teaserClosed = true;
247
+ }
248
+ };
249
+
250
+ let ticking = false;
251
+ document.addEventListener('scroll', () => {
252
+ const lastKnownScrollPosition = window.scrollY;
253
+ if (!ticking) {
254
+ window.requestAnimationFrame(() => {
255
+ doSomething(lastKnownScrollPosition);
256
+ ticking = false;
257
+ });
258
+ ticking = true;
259
+ }
260
+ });
261
+
262
+ this.activateLoginHandler();
263
+
264
+ // For common toast messages (not for callfunction toasts => handelt by own controller)
265
+ this.addEventListener("toast-event", this._createTemporaryToast);
266
+ this.addEventListener("route-event", this._handleMenuRouteEvent);
267
+ }
268
+
269
+ /**
270
+ * Non reactive private property for the index db class.
271
+ */
272
+ // private _indexDBClient?: YoIndexDBClient|null;
273
+
274
+ /**
275
+ * Needed for the router.
276
+ * Return all configured routes.
277
+ */
278
+ static get routes() {
279
+ return transformedRoutes;
25
280
  }
26
281
 
27
- render() {
282
+ /**
283
+ * Needed for the router.
284
+ * Set route params to internal members.
285
+ */
286
+ router(route:string, params:Object, query:Object) {
287
+ this.route = route;
288
+ this.params = params;
289
+ this.query = query;
290
+ }
291
+
292
+ /**
293
+ * Call to activate onAuthState change with profile request callbacks.
294
+ */
295
+ activateLoginHandler() {
296
+ // activate firebase auth handler
297
+ const auth = getAuth();
298
+ onAuthStateChanged(auth, user => {
299
+ if (user) {
300
+ this._user = user;
301
+ if (postLoginFunc) {
302
+
303
+ callFunction(postLoginFunc, undefined)
304
+ .then((result:any) => {
305
+ this._profile = result.resultData;
306
+ // redirect if login in the meantime is available
307
+ // => else stay on the login page after reload/url navigation
308
+ if (this.route === "login") {
309
+ this.navigate("profile");
310
+ }
311
+ });
312
+ }
313
+ } else {
314
+ this._user = undefined;
315
+ this._profile = undefined;
316
+ }
317
+ });
318
+ }
319
+
320
+ render() {
321
+ const pageConf = navigationConfig.pages.filter(p => p.name === this.route)[0];
322
+ const hideTeaser = this._teaserClosed || !pageConf?.withTeaser;
28
323
  return html`
29
- <h2>${this.title} Nr. ${this.counter}!</h2>
30
- <button @click=${this.__increment}>increment</button>
324
+ <header id="headerElementId" class="default-header">
325
+ ${hideTeaser ? '' : this._renderTeaser()}
326
+ ${this._renderMenu(pageConf, hideTeaser)}
327
+ </header>
328
+
329
+ <main class="${hideTeaser ? "default-main" : "default-main-withteaser"}">
330
+ <app-main active-route=${this.route}
331
+ @init-menu-sections="${this._initMenuSections}">
332
+ ${this._renderRoutePages()}
333
+ ${navigationConfig.includeLogin ? html`
334
+ <default-login route="login"></default-login>` : ''}
335
+ </app-main>
336
+ </main>
337
+
338
+ ${this.functionsController.render({
339
+ complete: (result: FunctionResult) => this._renderSuccessResultInfo(result),
340
+ initial: () => '',
341
+ pending: () => this._renderPendingInfo(),
342
+ error: (e: any) => this._renderErrorInfo(e),
343
+ })}
344
+
345
+ ${pageConf?.withFooter ? html`
346
+ <footer class="default-footer">
347
+ ${this._renderFooter()}
348
+ </footer>` : ''}
31
349
  `;
32
350
  }
33
- }
351
+
352
+ _renderMenu(pageConfig:NavigationPage, hideTeaser:boolean) {
353
+ const menuForPage = pageConfig ? (pageConfig.menu || []) : [];
354
+ return menuForPage ? html`
355
+ <pd-menu
356
+ class="${hideTeaser ? "default-menu" : "default-menu-withteaser"}"
357
+ .menuItems=${menuForPage.filter(mfp => !mfp.topItem)}
358
+ .topMenuItems=${menuForPage.filter(mfp => mfp.topItem)}
359
+ headerSize="${hideTeaser ? 80 : 440}"
360
+ >
361
+ ${hideTeaser ? this._getAppLogo() : ''}
362
+ </pd-menu>
363
+ ` : '';
364
+ }
365
+
366
+ // eslint-disable-next-line class-methods-use-this
367
+ _renderTeaser(): TemplateResult | string {
368
+ const teaserContent = this._getTeaserContent();
369
+ if (!teaserContent || teaserContent.length <= 0) {
370
+ return '';
371
+ }
372
+ return html`
373
+ <pd-panel-viewer class="default-teaser" id="teaserPanelViewerId" deltaCalc="4">
374
+ ${teaserContent.map(content => html`
375
+ <pd-panel>
376
+ ${content}
377
+ </pd-panel>
378
+ `)}
379
+ </pd-panel-viewer>
380
+ `;
381
+ }
382
+
383
+ _renderFooter() {
384
+ return html`
385
+ <pd-footer
386
+ class="default-footer"
387
+ .footerLinks="${this._getFooterItems()}"
388
+ version="0.5"
389
+ copyright="PD Progressive Developent UG"
390
+ .madeBy="${MADE_BY}"
391
+ @footer-link="${this._handleFooterRouteEvent}">
392
+ </pd-footer>
393
+ `;
394
+ }
395
+
396
+ /**
397
+ * Called when (any) cloud function call was finished.
398
+ */
399
+ // eslint-disable-next-line class-methods-use-this, @typescript-eslint/no-unused-vars
400
+ _renderSuccessResultInfo(result: FunctionResult) {
401
+ return html`
402
+ <pd-toast isSuccess duration="${TOAST_DURATION}">
403
+ ${this.functionsController.callDataPromise?.func.successTxt}
404
+ </pd-toast>`;
405
+ }
406
+
407
+ /**
408
+ * Called when (any) cloud function is currently running.
409
+ */
410
+ _renderPendingInfo() {
411
+ return this.functionsController.callDataPromise?.func.fadeWindow ? html`
412
+ <p>Überblende und lade ${this.functionsController.callDataPromise?.func.name}...</p>`
413
+ : html`
414
+ <pd-toast duration="-1">
415
+ ${this.functionsController.callDataPromise?.func.pendingTxt}
416
+ </pd-toast>`;
417
+ }
418
+
419
+ /**
420
+ * Called when (any) cloud function stopped with errors.
421
+ */
422
+ // eslint-disable-next-line class-methods-use-this
423
+ _renderErrorInfo(error:any) {
424
+ return html`
425
+ <pd-toast isError duration="-1">
426
+ ${error}
427
+ </pd-toast>`;
428
+ }
429
+
430
+ protected abstract _getTeaserContent(): Array<TemplateResult>;
431
+
432
+ protected abstract _getFooterItems(): Array<any>;
433
+
434
+ protected abstract _getAppLogo(): TemplateResult;
435
+
436
+ protected abstract _renderRoutePages(): any;
437
+
438
+ // eslint-disable-next-line class-methods-use-this
439
+ _handleMenuRouteEvent(e:any) {
440
+ // test with el, to handel jumps also here, not used at the moment, same postion link problems...
441
+ if (e.detail.el) {
442
+ this._scrollToContent(e.detail.el);
443
+ } else {
444
+ PdSpaHelper._scrollToTop();
445
+ this.navigate(e.detail.route);
446
+ }
447
+ }
448
+
449
+ // eslint-disable-next-line class-methods-use-this
450
+ _handleFooterRouteEvent(e: CustomEvent) {
451
+ PdSpaHelper._scrollToTop();
452
+ this.navigate(e.detail.link);
453
+ }
454
+
455
+ // eslint-disable-next-line class-methods-use-this
456
+ _initMenuSections(e:any) {
457
+ // get page for event
458
+ const menRefConf = navigationConfig.pages.filter(p => p.name === e.detail.key)[0];
459
+ if (menRefConf) {
460
+ menRefConf.menu = e.detail.menu;
461
+ }
462
+ }
463
+
464
+ _createTemporaryToast(e: any) {
465
+
466
+ // hide existing call function toast (old one still is visible for directly incoming errors)
467
+ this.functionsController.clear();
468
+
469
+ const tmpToast = new PdToast();
470
+ tmpToast.isError = e.detail.isError;
471
+ tmpToast.isSuccess = e.detail.isSuccess;
472
+ const slotContent = document.createTextNode(e.detail.txt);
473
+ tmpToast.appendChild(slotContent)
474
+
475
+ this.shadowRoot?.appendChild(tmpToast);
476
+ tmpToast.duration = TOAST_DURATION;
477
+
478
+ // set timeout to remove toast (one second later)
479
+ setTimeout(() => {
480
+ this.shadowRoot?.removeChild(tmpToast);
481
+ }, TOAST_DURATION + 1000);
482
+ }
483
+
484
+ static _scrollToTop() {
485
+ window.scrollTo({
486
+ top: 0,
487
+ left: 0,
488
+ behavior: 'smooth',
489
+ });
490
+ }
491
+
492
+ // Test, not used at the moment, jump/menu problem...
493
+ // eslint-disable-next-line class-methods-use-this
494
+ _scrollToContent(el:HTMLElement) {
495
+ if (el) {
496
+ const rect = el.getBoundingClientRect();
497
+ console.log("Rect: ", rect);
498
+ console.log("Offset Top/Height: ", el.offsetTop, el.offsetHeight);
499
+ window.scrollBy({
500
+ top: rect.top - (this._teaserClosed ? 80 : 450),
501
+ left: 0,
502
+ behavior: 'smooth',
503
+ });
504
+ }
505
+ }
506
+
507
+ }
@@ -0,0 +1,15 @@
1
+ import { html, LitElement } from "lit";
2
+ import { customElement } from "lit/decorators.js";
3
+
4
+ import '../tmpown/pd-login.js';
5
+
6
+ @customElement("default-login")
7
+ export class DefaultLogin extends LitElement {
8
+
9
+ render() {
10
+ return html`
11
+ <pd-login></pd-login>
12
+ `;
13
+ }
14
+
15
+ }
@@ -0,0 +1,30 @@
1
+ import { getAuth, signInWithEmailAndPassword, signOut, User } from 'firebase/auth';
2
+
3
+ export const logout = async (): Promise<boolean> => {
4
+ const auth = getAuth();
5
+ try {
6
+ await signOut(auth);
7
+ return true;
8
+ } catch (error) {
9
+ return false;
10
+ }
11
+ };
12
+
13
+ export const isAuthenticated = (): boolean =>
14
+ getAuth().currentUser !== null;
15
+
16
+ export const login = async (user:string, sec:string): Promise<User> => {
17
+ if (!user) {
18
+ const returnError = new Error("empty user");
19
+ return Promise.reject(returnError);
20
+ }
21
+ if (!sec) {
22
+ // error.eType = 'sec';
23
+ // error.logMsg = 'password required';
24
+ const returnError = new Error("empty password");
25
+ return Promise.reject(returnError);
26
+ }
27
+ const auth = getAuth();
28
+ const credentials = await signInWithEmailAndPassword(auth, user, sec);
29
+ return credentials.user;
30
+ }
@@ -0,0 +1,21 @@
1
+ import { FirebaseApp } from 'firebase/app';
2
+ import {Firestore, getFirestore} from 'firebase/firestore';
3
+ // import { getAuth } from 'firebase/auth';
4
+
5
+ let db:Firestore;
6
+
7
+ /**
8
+ * During start/load application, initialize functions.
9
+ *
10
+ * @param {*} app - initialized app.
11
+ */
12
+ export const initFirestore = (app: FirebaseApp) => {
13
+ try {
14
+ // init db access
15
+ db = getFirestore(app);
16
+ } catch (error) {
17
+ console.error(error);
18
+ }
19
+ };
20
+
21
+ export const getDB = ():Firestore => db;
@@ -0,0 +1,103 @@
1
+ import { FirebaseApp } from "firebase/app";
2
+ import { getFunctions, HttpsCallable, httpsCallable } from "firebase/functions";
3
+ import { TemplateResult } from "lit";
4
+ import { ServiceCallController } from "../service-call-controller2.js";
5
+
6
+ export interface FunctionDefinition {
7
+ name: string,
8
+ successCodes: Array<Number>,
9
+ // Fade window during function call (true) or show only background infos (false).
10
+ fadeWindow: boolean,
11
+ successTxt: TemplateResult,
12
+ pendingTxt: TemplateResult
13
+ }
14
+
15
+ export interface FunctionParam {
16
+ func: FunctionDefinition,
17
+ inputData: any,
18
+ }
19
+
20
+ export interface FunctionResult {
21
+ resultData: unknown,
22
+ statusCode: number,
23
+ }
24
+
25
+ export interface FunctionsConfig {
26
+ region: string, // 'europe-west3'
27
+ functions: Array<FunctionDefinition>,
28
+ postLoginFunc?: FunctionDefinition,
29
+ }
30
+
31
+ const functionMap = new Map<FunctionDefinition, HttpsCallable>;
32
+ let controller: ServiceCallController;
33
+
34
+ /**
35
+ * During start/load application, initialize functions.
36
+ *
37
+ * @param {*} app - initialized app.
38
+ */
39
+ export const initFunctions = (app: FirebaseApp, functionsConfig: FunctionsConfig) => {
40
+ try {
41
+ // Initialize cloud functions through Firebase
42
+ const functions = getFunctions(app, functionsConfig.region);
43
+
44
+ // Initialize functions map
45
+ functionsConfig.functions.forEach(func => {
46
+ functionMap.set(func, httpsCallable(functions, func.name));
47
+ });
48
+
49
+ } catch (error) {
50
+ console.error("ToDo: Error programmieren...", error);
51
+ }
52
+ }
53
+
54
+ export const initController = (controllerParam: ServiceCallController) => {
55
+ controller = controllerParam
56
+ }
57
+
58
+ const internalCallFunction = async (def: FunctionDefinition, functionInput: any): Promise<FunctionResult> => {
59
+ const funcRef = functionMap.get(def);
60
+ if (funcRef) {
61
+ const funcResult:any = await funcRef(functionInput);
62
+ if (funcResult
63
+ && funcResult.data && def.successCodes.includes(funcResult.data.statusCode)) {
64
+ return funcResult.data as FunctionResult;
65
+ }
66
+ /*
67
+ const ne = new Error("Invalid result");
68
+ if (result.data) {
69
+ // Read result of the Cloud Function.
70
+ switch (result.data.statusCode) {
71
+ case 200:
72
+ return resolve(result.data.projects);
73
+ default:
74
+ ne.data = result.data;
75
+ return reject(ne);
76
+ }
77
+ } else {
78
+ return reject(ne);
79
+ }
80
+ })
81
+ .catch((error) => {
82
+ // Getting the Error details.
83
+ const {code} = error;
84
+ const {message} = error;
85
+ const {details} = error;
86
+
87
+ console.warn("Error occured: ", code, message, details);
88
+ console.warn("Error: ", error);
89
+ return reject(error);
90
+ })
91
+ */
92
+ }
93
+ console.log("No function available for ", def.name);
94
+ throw new Error("Illegal state, no data with right state available");
95
+ }
96
+
97
+ export const callFunction = async (def: FunctionDefinition, functionInput: any): Promise<FunctionResult> => {
98
+ const promise = internalCallFunction(def, functionInput);
99
+ if (controller) {
100
+ controller.callDataPromise = {func: def, promise};
101
+ }
102
+ return promise;
103
+ }
package/src/index.ts CHANGED
@@ -1 +1 @@
1
- export { PdSpaHelper } from './PdSpaHelper.js';
1
+ export { PdSpaHelper, startInit, NavigationConfig, NavigationPage, MenuElement } from './PdSpaHelper.js';
@@ -1,3 +1,3 @@
1
- import { PdSpaHelper } from './PdSpaHelper.js';
2
-
3
- window.customElements.define('pd-spa-helper', PdSpaHelper);
1
+ //import { PdSpaHelper } from './PdSpaHelper.js';
2
+ //export default PdSpaHelper;
3
+ //window.customElements.define('pd-spa-helper', PdSpaHelper);