1 | import * as i0 from '@angular/core';
|
2 | import { InjectionToken, PLATFORM_ID, Injectable, Inject, Optional, NgModule } from '@angular/core';
|
3 | import { asyncScheduler, Observable, of, merge } from 'rxjs';
|
4 | import { map, share, switchMap, scan, distinctUntilChanged, withLatestFrom, skipWhile } from 'rxjs/operators';
|
5 | import * as i1 from '@angular/fire';
|
6 | import { keepUnstableUntilFirst, VERSION } from '@angular/fire';
|
7 | import { ɵfirebaseAppFactory, ɵcacheInstance, FIREBASE_OPTIONS, FIREBASE_APP_NAME } from '@angular/fire/compat';
|
8 | import 'firebase/compat/auth';
|
9 | import 'firebase/compat/database';
|
10 | import * as i2 from '@angular/fire/compat/auth';
|
11 | import { ɵauthFactory, USE_EMULATOR as USE_EMULATOR$1, SETTINGS, TENANT_ID, LANGUAGE_CODE, USE_DEVICE_LANGUAGE, PERSISTENCE } from '@angular/fire/compat/auth';
|
12 | import * as i3 from '@angular/fire/app-check';
|
13 | import firebase from 'firebase/compat/app';
|
14 |
|
15 | function isString(value) {
|
16 | return typeof value === 'string';
|
17 | }
|
18 | function isFirebaseDataSnapshot(value) {
|
19 | return typeof value.exportVal === 'function';
|
20 | }
|
21 | function isNil(obj) {
|
22 | return obj === undefined || obj === null;
|
23 | }
|
24 | function isFirebaseRef(value) {
|
25 | return typeof value.set === 'function';
|
26 | }
|
27 |
|
28 |
|
29 |
|
30 |
|
31 |
|
32 |
|
33 | function getRef(database, pathRef) {
|
34 |
|
35 | return isFirebaseRef(pathRef) ? pathRef
|
36 | : database.ref(pathRef);
|
37 | }
|
38 | function checkOperationCases(item, cases) {
|
39 | if (isString(item)) {
|
40 | return cases.stringCase();
|
41 | }
|
42 | else if (isFirebaseRef(item)) {
|
43 | return cases.firebaseCase();
|
44 | }
|
45 | else if (isFirebaseDataSnapshot(item)) {
|
46 | return cases.snapshotCase();
|
47 | }
|
48 | throw new Error(`Expects a string, snapshot, or reference. Got: ${typeof item}`);
|
49 | }
|
50 |
|
51 |
|
52 |
|
53 |
|
54 |
|
55 |
|
56 |
|
57 |
|
58 | function fromRef(ref, event, listenType = 'on', scheduler = asyncScheduler) {
|
59 | return new Observable(subscriber => {
|
60 | let fn = null;
|
61 | fn = ref[listenType](event, (snapshot, prevKey) => {
|
62 | scheduler.schedule(() => {
|
63 | subscriber.next({ snapshot, prevKey });
|
64 | });
|
65 | if (listenType === 'once') {
|
66 | scheduler.schedule(() => subscriber.complete());
|
67 | }
|
68 | }, err => {
|
69 | scheduler.schedule(() => subscriber.error(err));
|
70 | });
|
71 | if (listenType === 'on') {
|
72 | return {
|
73 | unsubscribe() {
|
74 | if (fn != null) {
|
75 | ref.off(event, fn);
|
76 | }
|
77 | }
|
78 | };
|
79 | }
|
80 | else {
|
81 | return {
|
82 | unsubscribe() {
|
83 | }
|
84 | };
|
85 | }
|
86 | }).pipe(map(payload => {
|
87 | const { snapshot, prevKey } = payload;
|
88 | let key = null;
|
89 | if (snapshot.exists()) {
|
90 | key = snapshot.key;
|
91 | }
|
92 | return { type: event, payload: snapshot, prevKey, key };
|
93 | }), share());
|
94 | }
|
95 |
|
96 | function listChanges(ref, events, scheduler) {
|
97 | return fromRef(ref, 'value', 'once', scheduler).pipe(switchMap(snapshotAction => {
|
98 | const childEvent$ = [of(snapshotAction)];
|
99 | events.forEach(event => childEvent$.push(fromRef(ref, event, 'on', scheduler)));
|
100 | return merge(...childEvent$).pipe(scan(buildView, []));
|
101 | }), distinctUntilChanged());
|
102 | }
|
103 | function positionFor(changes, key) {
|
104 | const len = changes.length;
|
105 | for (let i = 0; i < len; i++) {
|
106 | if (changes[i].payload.key === key) {
|
107 | return i;
|
108 | }
|
109 | }
|
110 | return -1;
|
111 | }
|
112 | function positionAfter(changes, prevKey) {
|
113 | if (isNil(prevKey)) {
|
114 | return 0;
|
115 | }
|
116 | else {
|
117 | const i = positionFor(changes, prevKey);
|
118 | if (i === -1) {
|
119 | return changes.length;
|
120 | }
|
121 | else {
|
122 | return i + 1;
|
123 | }
|
124 | }
|
125 | }
|
126 | function buildView(current, action) {
|
127 | const { payload, prevKey, key } = action;
|
128 | const currentKeyPosition = positionFor(current, key);
|
129 | const afterPreviousKeyPosition = positionAfter(current, prevKey);
|
130 | switch (action.type) {
|
131 | case 'value':
|
132 | if (action.payload && action.payload.exists()) {
|
133 | let prevKey = null;
|
134 | action.payload.forEach(payload => {
|
135 | const action = { payload, type: 'value', prevKey, key: payload.key };
|
136 | prevKey = payload.key;
|
137 | current = [...current, action];
|
138 | return false;
|
139 | });
|
140 | }
|
141 | return current;
|
142 | case 'child_added':
|
143 | if (currentKeyPosition > -1) {
|
144 |
|
145 | const previous = current[currentKeyPosition - 1];
|
146 | if ((previous && previous.key || null) !== prevKey) {
|
147 | current = current.filter(x => x.payload.key !== payload.key);
|
148 | current.splice(afterPreviousKeyPosition, 0, action);
|
149 | }
|
150 | }
|
151 | else if (prevKey == null) {
|
152 | return [action, ...current];
|
153 | }
|
154 | else {
|
155 | current = current.slice();
|
156 | current.splice(afterPreviousKeyPosition, 0, action);
|
157 | }
|
158 | return current;
|
159 | case 'child_removed':
|
160 | return current.filter(x => x.payload.key !== payload.key);
|
161 | case 'child_changed':
|
162 | return current.map(x => x.payload.key === key ? action : x);
|
163 | case 'child_moved':
|
164 | if (currentKeyPosition > -1) {
|
165 | const data = current.splice(currentKeyPosition, 1)[0];
|
166 | current = current.slice();
|
167 | current.splice(afterPreviousKeyPosition, 0, data);
|
168 | return current;
|
169 | }
|
170 | return current;
|
171 |
|
172 | default:
|
173 | return current;
|
174 | }
|
175 | }
|
176 |
|
177 | function validateEventsArray(events) {
|
178 | if (isNil(events) || events.length === 0) {
|
179 | events = ['child_added', 'child_removed', 'child_changed', 'child_moved'];
|
180 | }
|
181 | return events;
|
182 | }
|
183 |
|
184 | function snapshotChanges(query, events, scheduler) {
|
185 | events = validateEventsArray(events);
|
186 | return listChanges(query, events, scheduler);
|
187 | }
|
188 |
|
189 | function stateChanges(query, events, scheduler) {
|
190 | events = validateEventsArray(events);
|
191 | const childEvent$ = events.map(event => fromRef(query, event, 'on', scheduler));
|
192 | return merge(...childEvent$);
|
193 | }
|
194 |
|
195 | function auditTrail(query, events, scheduler) {
|
196 | const auditTrail$ = stateChanges(query, events)
|
197 | .pipe(scan((current, action) => [...current, action], []));
|
198 | return waitForLoaded(query, auditTrail$, scheduler);
|
199 | }
|
200 | function loadedData(query, scheduler) {
|
201 |
|
202 |
|
203 |
|
204 | return fromRef(query, 'value', 'on', scheduler)
|
205 | .pipe(map(data => {
|
206 |
|
207 | let lastKeyToLoad;
|
208 |
|
209 | data.payload.forEach(child => {
|
210 | lastKeyToLoad = child.key;
|
211 | return false;
|
212 | });
|
213 |
|
214 | return { data, lastKeyToLoad };
|
215 | }));
|
216 | }
|
217 | function waitForLoaded(query, action$, scheduler) {
|
218 | const loaded$ = loadedData(query, scheduler);
|
219 | return loaded$
|
220 | .pipe(withLatestFrom(action$),
|
221 |
|
222 |
|
223 | map(([loaded, actions]) => {
|
224 |
|
225 | const lastKeyToLoad = loaded.lastKeyToLoad;
|
226 |
|
227 | const loadedKeys = actions.map(snap => snap.key);
|
228 | return { actions, lastKeyToLoad, loadedKeys };
|
229 | }),
|
230 |
|
231 |
|
232 |
|
233 | skipWhile(meta => meta.loadedKeys.indexOf(meta.lastKeyToLoad) === -1),
|
234 |
|
235 |
|
236 | map(meta => meta.actions));
|
237 | }
|
238 |
|
239 | function createDataOperationMethod(ref, operation) {
|
240 | return function dataOperation(item, value) {
|
241 | return checkOperationCases(item, {
|
242 | stringCase: () => ref.child(item)[operation](value),
|
243 | firebaseCase: () => item[operation](value),
|
244 | snapshotCase: () => item.ref[operation](value)
|
245 | });
|
246 | };
|
247 | }
|
248 |
|
249 |
|
250 |
|
251 | function createRemoveMethod(ref) {
|
252 | return function remove(item) {
|
253 | if (!item) {
|
254 | return ref.remove();
|
255 | }
|
256 | return checkOperationCases(item, {
|
257 | stringCase: () => ref.child(item).remove(),
|
258 | firebaseCase: () => item.remove(),
|
259 | snapshotCase: () => item.ref.remove()
|
260 | });
|
261 | };
|
262 | }
|
263 |
|
264 | function createListReference(query, afDatabase) {
|
265 | const outsideAngularScheduler = afDatabase.schedulers.outsideAngular;
|
266 | const refInZone = afDatabase.schedulers.ngZone.run(() => query.ref);
|
267 | return {
|
268 | query,
|
269 | update: createDataOperationMethod(refInZone, 'update'),
|
270 | set: createDataOperationMethod(refInZone, 'set'),
|
271 | push: (data) => refInZone.push(data),
|
272 | remove: createRemoveMethod(refInZone),
|
273 | snapshotChanges(events) {
|
274 | return snapshotChanges(query, events, outsideAngularScheduler).pipe(keepUnstableUntilFirst);
|
275 | },
|
276 | stateChanges(events) {
|
277 | return stateChanges(query, events, outsideAngularScheduler).pipe(keepUnstableUntilFirst);
|
278 | },
|
279 | auditTrail(events) {
|
280 | return auditTrail(query, events, outsideAngularScheduler).pipe(keepUnstableUntilFirst);
|
281 | },
|
282 | valueChanges(events, options) {
|
283 | const snapshotChanges$ = snapshotChanges(query, events, outsideAngularScheduler);
|
284 | return snapshotChanges$.pipe(map(actions => actions.map(a => {
|
285 | if (options && options.idField) {
|
286 | return Object.assign(Object.assign({}, a.payload.val()), {
|
287 | [options.idField]: a.key
|
288 | });
|
289 | }
|
290 | else {
|
291 | return a.payload.val();
|
292 | }
|
293 | })), keepUnstableUntilFirst);
|
294 | }
|
295 | };
|
296 | }
|
297 |
|
298 | function createObjectSnapshotChanges(query, scheduler) {
|
299 | return function snapshotChanges() {
|
300 | return fromRef(query, 'value', 'on', scheduler);
|
301 | };
|
302 | }
|
303 |
|
304 | function createObjectReference(query, afDatabase) {
|
305 | return {
|
306 | query,
|
307 | snapshotChanges() {
|
308 | return createObjectSnapshotChanges(query, afDatabase.schedulers.outsideAngular)().pipe(keepUnstableUntilFirst);
|
309 | },
|
310 | update(data) { return query.ref.update(data); },
|
311 | set(data) { return query.ref.set(data); },
|
312 | remove() { return query.ref.remove(); },
|
313 | valueChanges() {
|
314 | const snapshotChanges$ = createObjectSnapshotChanges(query, afDatabase.schedulers.outsideAngular)();
|
315 | return snapshotChanges$.pipe(keepUnstableUntilFirst, map(action => action.payload.exists() ? action.payload.val() : null));
|
316 | },
|
317 | };
|
318 | }
|
319 |
|
320 | const URL = new InjectionToken('angularfire2.realtimeDatabaseURL');
|
321 | const USE_EMULATOR = new InjectionToken('angularfire2.database.use-emulator');
|
322 | class AngularFireDatabase {
|
323 | constructor(options, name, databaseURL,
|
324 | // tslint:disable-next-line:ban-types
|
325 | platformId, zone, schedulers, _useEmulator, // tuple isn't working here
|
326 | auth, useAuthEmulator, authSettings, // can't use firebase.auth.AuthSettings here
|
327 | tenantId, languageCode, useDeviceLanguage, persistence, _appCheckInstances) {
|
328 | this.schedulers = schedulers;
|
329 | const useEmulator = _useEmulator;
|
330 | const app = ɵfirebaseAppFactory(options, zone, name);
|
331 | if (auth) {
|
332 | ɵauthFactory(app, zone, useAuthEmulator, tenantId, languageCode, useDeviceLanguage, authSettings, persistence);
|
333 | }
|
334 | this.database = ɵcacheInstance(`${app.name}.database.${databaseURL}`, 'AngularFireDatabase', app.name, () => {
|
335 | const database = zone.runOutsideAngular(() => app.database(databaseURL || undefined));
|
336 | if (useEmulator) {
|
337 | database.useEmulator(...useEmulator);
|
338 | }
|
339 | return database;
|
340 | }, [useEmulator]);
|
341 | }
|
342 | list(pathOrRef, queryFn) {
|
343 | const ref = this.schedulers.ngZone.runOutsideAngular(() => getRef(this.database, pathOrRef));
|
344 | let query = ref;
|
345 | if (queryFn) {
|
346 | query = queryFn(ref);
|
347 | }
|
348 | return createListReference(query, this);
|
349 | }
|
350 | object(pathOrRef) {
|
351 | const ref = this.schedulers.ngZone.runOutsideAngular(() => getRef(this.database, pathOrRef));
|
352 | return createObjectReference(ref, this);
|
353 | }
|
354 | createPushId() {
|
355 | const ref = this.schedulers.ngZone.runOutsideAngular(() => this.database.ref());
|
356 | return ref.push().key;
|
357 | }
|
358 | }
|
359 | AngularFireDatabase.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.1.3", ngImport: i0, type: AngularFireDatabase, deps: [{ token: FIREBASE_OPTIONS }, { token: FIREBASE_APP_NAME, optional: true }, { token: URL, optional: true }, { token: PLATFORM_ID }, { token: i0.NgZone }, { token: i1.ɵAngularFireSchedulers }, { token: USE_EMULATOR, optional: true }, { token: i2.AngularFireAuth, optional: true }, { token: USE_EMULATOR$1, optional: true }, { token: SETTINGS, optional: true }, { token: TENANT_ID, optional: true }, { token: LANGUAGE_CODE, optional: true }, { token: USE_DEVICE_LANGUAGE, optional: true }, { token: PERSISTENCE, optional: true }, { token: i3.AppCheckInstances, optional: true }], target: i0.ɵɵFactoryTarget.Injectable });
|
360 | AngularFireDatabase.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "12.1.3", ngImport: i0, type: AngularFireDatabase, providedIn: 'any' });
|
361 | i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.1.3", ngImport: i0, type: AngularFireDatabase, decorators: [{
|
362 | type: Injectable,
|
363 | args: [{
|
364 | providedIn: 'any'
|
365 | }]
|
366 | }], ctorParameters: function () { return [{ type: undefined, decorators: [{
|
367 | type: Inject,
|
368 | args: [FIREBASE_OPTIONS]
|
369 | }] }, { type: undefined, decorators: [{
|
370 | type: Optional
|
371 | }, {
|
372 | type: Inject,
|
373 | args: [FIREBASE_APP_NAME]
|
374 | }] }, { type: undefined, decorators: [{
|
375 | type: Optional
|
376 | }, {
|
377 | type: Inject,
|
378 | args: [URL]
|
379 | }] }, { type: Object, decorators: [{
|
380 | type: Inject,
|
381 | args: [PLATFORM_ID]
|
382 | }] }, { type: i0.NgZone }, { type: i1.ɵAngularFireSchedulers }, { type: undefined, decorators: [{
|
383 | type: Optional
|
384 | }, {
|
385 | type: Inject,
|
386 | args: [USE_EMULATOR]
|
387 | }] }, { type: i2.AngularFireAuth, decorators: [{
|
388 | type: Optional
|
389 | }] }, { type: undefined, decorators: [{
|
390 | type: Optional
|
391 | }, {
|
392 | type: Inject,
|
393 | args: [USE_EMULATOR$1]
|
394 | }] }, { type: undefined, decorators: [{
|
395 | type: Optional
|
396 | }, {
|
397 | type: Inject,
|
398 | args: [SETTINGS]
|
399 | }] }, { type: undefined, decorators: [{
|
400 | type: Optional
|
401 | }, {
|
402 | type: Inject,
|
403 | args: [TENANT_ID]
|
404 | }] }, { type: undefined, decorators: [{
|
405 | type: Optional
|
406 | }, {
|
407 | type: Inject,
|
408 | args: [LANGUAGE_CODE]
|
409 | }] }, { type: undefined, decorators: [{
|
410 | type: Optional
|
411 | }, {
|
412 | type: Inject,
|
413 | args: [USE_DEVICE_LANGUAGE]
|
414 | }] }, { type: undefined, decorators: [{
|
415 | type: Optional
|
416 | }, {
|
417 | type: Inject,
|
418 | args: [PERSISTENCE]
|
419 | }] }, { type: i3.AppCheckInstances, decorators: [{
|
420 | type: Optional
|
421 | }] }]; } });
|
422 |
|
423 | class AngularFireDatabaseModule {
|
424 | constructor() {
|
425 | firebase.registerVersion('angularfire', VERSION.full, 'rtdb-compat');
|
426 | }
|
427 | }
|
428 | AngularFireDatabaseModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.1.3", ngImport: i0, type: AngularFireDatabaseModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
|
429 | AngularFireDatabaseModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "12.0.0", version: "12.1.3", ngImport: i0, type: AngularFireDatabaseModule });
|
430 | AngularFireDatabaseModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "12.1.3", ngImport: i0, type: AngularFireDatabaseModule, providers: [AngularFireDatabase] });
|
431 | i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.1.3", ngImport: i0, type: AngularFireDatabaseModule, decorators: [{
|
432 | type: NgModule,
|
433 | args: [{
|
434 | providers: [AngularFireDatabase]
|
435 | }]
|
436 | }], ctorParameters: function () { return []; } });
|
437 |
|
438 |
|
439 |
|
440 |
|
441 |
|
442 | export { AngularFireDatabase, AngularFireDatabaseModule, URL, USE_EMULATOR, auditTrail, createListReference, fromRef, listChanges, snapshotChanges, stateChanges };
|
443 |
|