@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.
- package/dist/src/InitApplicationData.d.ts +3 -0
- package/dist/src/InitApplicationData.js +8 -0
- package/dist/src/InitApplicationData.js.map +1 -0
- package/dist/src/PdSpaHelper.d.ts +111 -6
- package/dist/src/PdSpaHelper.js +410 -12
- package/dist/src/PdSpaHelper.js.map +1 -1
- package/dist/src/defaultpage/default-login.d.ts +5 -0
- package/dist/src/defaultpage/default-login.js +16 -0
- package/dist/src/defaultpage/default-login.js.map +1 -0
- package/dist/src/firebase/auth.d.ts +4 -0
- package/dist/src/firebase/auth.js +28 -0
- package/dist/src/firebase/auth.js.map +1 -0
- package/dist/src/firebase/firestore-client.d.ts +9 -0
- package/dist/src/firebase/firestore-client.js +19 -0
- package/dist/src/firebase/firestore-client.js.map +1 -0
- package/dist/src/firebase/functions-client.d.ts +31 -0
- package/dist/src/firebase/functions-client.js +70 -0
- package/dist/src/firebase/functions-client.js.map +1 -0
- package/dist/src/index.d.ts +1 -1
- package/dist/src/index.js +1 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/pd-spa-helper.d.ts +0 -1
- package/dist/src/pd-spa-helper.js +4 -2
- package/dist/src/pd-spa-helper.js.map +1 -1
- package/dist/src/router/AppMain.d.ts +6 -0
- package/dist/src/router/AppMain.js +14 -0
- package/dist/src/router/AppMain.js.map +1 -0
- package/dist/src/service-call-controller2.d.ts +16 -0
- package/dist/src/service-call-controller2.js +43 -0
- package/dist/src/service-call-controller2.js.map +1 -0
- package/dist/src/tmpown/pd-login.d.ts +14 -0
- package/dist/src/tmpown/pd-login.js +118 -0
- package/dist/src/tmpown/pd-login.js.map +1 -0
- package/dist/src/tmpown/pd-panel-viewer.d.ts +18 -0
- package/dist/src/tmpown/pd-panel-viewer.js +187 -0
- package/dist/src/tmpown/pd-panel-viewer.js.map +1 -0
- package/dist/src/tmpown/pd-panel.d.ts +5 -0
- package/dist/src/tmpown/pd-panel.js +41 -0
- package/dist/src/tmpown/pd-panel.js.map +1 -0
- package/dist/src/tmpown/pd-toast.d.ts +12 -0
- package/dist/src/tmpown/pd-toast.js +114 -0
- package/dist/src/tmpown/pd-toast.js.map +1 -0
- package/dist/test/pd-spa-helper.test.js +2 -2
- package/dist/test/pd-spa-helper.test.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +7 -1
- package/pd-spa-helper.iml +9 -0
- package/src/InitApplicationData.ts +9 -0
- package/src/PdSpaHelper.ts +490 -16
- package/src/defaultpage/default-login.ts +15 -0
- package/src/firebase/auth.ts +30 -0
- package/src/firebase/firestore-client.ts +21 -0
- package/src/firebase/functions-client.ts +103 -0
- package/src/index.ts +1 -1
- package/src/pd-spa-helper.ts +3 -3
- package/src/router/AppMain.ts +10 -0
- package/src/service-call-controller2.ts +67 -0
- package/src/tmpown/pd-login.ts +126 -0
- package/src/tmpown/pd-panel-viewer.ts +193 -0
- package/src/tmpown/pd-panel.ts +43 -0
- package/src/tmpown/pd-toast.ts +114 -0
- package/test/pd-spa-helper.test.ts +2 -2
package/src/PdSpaHelper.ts
CHANGED
|
@@ -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,
|
|
8
|
+
import { html, LitElement, css, CSSResultGroup, TemplateResult } from 'lit';
|
|
7
9
|
import { property } from 'lit/decorators.js';
|
|
8
10
|
|
|
9
|
-
|
|
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
|
-
|
|
15
|
-
color: var(--pd-spa-helper-text-color, #000);
|
|
204
|
+
z-index: 99;
|
|
16
205
|
}
|
|
17
|
-
`;
|
|
18
206
|
|
|
19
|
-
|
|
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
|
-
|
|
238
|
+
constructor() {
|
|
22
239
|
|
|
23
|
-
|
|
24
|
-
|
|
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
|
-
|
|
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
|
-
<
|
|
30
|
-
|
|
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';
|
package/src/pd-spa-helper.ts
CHANGED
|
@@ -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);
|