UNPKG

3.32 kBJavaScriptView Raw
1import angular from './angularCompatibility.js';
2import Vue from 'vue';
3import Reference from './Reference.js';
4
5
6export default class MetaTree {
7 constructor(rootUrl, tree, bridge, dispatcher) {
8 this._rootUrl = rootUrl;
9 this._tree = tree;
10 this._dispatcher = dispatcher;
11 this._bridge = bridge;
12 this._vue = new Vue({data: {$root: {
13 connected: undefined, timeOffset: 0, user: undefined, userid: undefined,
14 nowAtInterval(intervalMillis) {
15 const key = 'now' + intervalMillis;
16 if (!this.hasOwnProperty(key)) {
17 const update = () => {
18 Vue.set(this, key, Date.now() + this.timeOffset);
19 angular.digest();
20 };
21 update();
22 setInterval(update, intervalMillis);
23 }
24 return this[key];
25 }
26 }}});
27
28 this._auth = {serial: 0, initialAuthChangeReceived: false};
29
30 bridge.onAuth(rootUrl, this._handleAuthChange, this);
31
32 this._connectInfoProperty('serverTimeOffset', 'timeOffset');
33 this._connectInfoProperty('connected', 'connected');
34 Object.freeze(this);
35 }
36
37 get root() {
38 return this._vue.$data.$root;
39 }
40
41 destroy() {
42 this._bridge.offAuth(this._rootUrl, this._handleAuthChange, this);
43 this._vue.$destroy();
44 }
45
46 authenticate(token) {
47 this._auth.serial++;
48 return this._dispatcher.execute(
49 'auth', 'authenticate', new Reference(this._tree, '/'), token, () => {
50 return this._bridge.authWithCustomToken(this._rootUrl, token);
51 }
52 );
53 }
54
55 unauthenticate() {
56 // Signal user change to null pre-emptively. This is what the Firebase SDK does as well, since
57 // it lets the app tear down user-required connections before the user is actually deauthed,
58 // which can prevent spurious permission denied errors.
59 this._auth.serial++;
60 return this._handleAuthChange(null).then(approved => {
61 // Bail if auth change callback initiated another authentication, since it will have already
62 // sent the command to the bridge and sending our own now would incorrectly override it.
63 if (!approved) return;
64 return this._dispatcher.execute(
65 'auth', 'unauthenticate', new Reference(this._tree, '/'), undefined, () => {
66 return this._bridge.unauth(this._rootUrl);
67 }
68 );
69 });
70 }
71
72 _handleAuthChange(user) {
73 const supersededChange = !this._auth.initialAuthChangeReceived && this._auth.serial;
74 if (user !== undefined) this._auth.initialAuthChangeReceived = true;
75 if (supersededChange) return;
76 const authSerial = this._auth.serial;
77 if (this.root.user === user) return Promise.resolve(false);
78 return this._dispatcher.execute('auth', 'certify', new Reference(this._tree, '/'), user, () => {
79 if (this.root.user === user || authSerial !== this._auth.serial) return false;
80 if (user) Object.freeze(user);
81 this.root.user = user;
82 this.root.userid = user && user.uid;
83 angular.digest();
84 return true;
85 });
86 }
87
88 _isAuthChangeStale(user) {
89 return this.root.user === user;
90 }
91
92 _connectInfoProperty(property, attribute) {
93 const propertyUrl = `${this._rootUrl}/.info/${property}`;
94 this._bridge.on(propertyUrl, propertyUrl, null, 'value', snap => {
95 this.root[attribute] = snap.value;
96 angular.digest();
97 });
98 }
99}