UNPKG

3.39 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 if (token) return this._bridge.authWithCustomToken(this._rootUrl, token);
51 return this._bridge.authAnonymously(this._rootUrl);
52 }
53 );
54 }
55
56 unauthenticate() {
57 // Signal user change to null pre-emptively. This is what the Firebase SDK does as well, since
58 // it lets the app tear down user-required connections before the user is actually deauthed,
59 // which can prevent spurious permission denied errors.
60 this._auth.serial++;
61 return this._handleAuthChange(null).then(approved => {
62 // Bail if auth change callback initiated another authentication, since it will have already
63 // sent the command to the bridge and sending our own now would incorrectly override it.
64 if (!approved) return;
65 return this._dispatcher.execute(
66 'auth', 'unauthenticate', new Reference(this._tree, '/'), undefined, () => {
67 return this._bridge.unauth(this._rootUrl);
68 }
69 );
70 });
71 }
72
73 _handleAuthChange(user) {
74 const supersededChange = !this._auth.initialAuthChangeReceived && this._auth.serial;
75 if (user !== undefined) this._auth.initialAuthChangeReceived = true;
76 if (supersededChange) return;
77 const authSerial = this._auth.serial;
78 if (this.root.user === user) return Promise.resolve(false);
79 return this._dispatcher.execute('auth', 'certify', new Reference(this._tree, '/'), user, () => {
80 if (this.root.user === user || authSerial !== this._auth.serial) return false;
81 if (user) Object.freeze(user);
82 this.root.user = user;
83 this.root.userid = user && user.uid;
84 angular.digest();
85 return true;
86 });
87 }
88
89 _isAuthChangeStale(user) {
90 return this.root.user === user;
91 }
92
93 _connectInfoProperty(property, attribute) {
94 const propertyUrl = `${this._rootUrl}/.info/${property}`;
95 this._bridge.on(propertyUrl, propertyUrl, null, 'value', snap => {
96 this.root[attribute] = snap.value;
97 angular.digest();
98 });
99 }
100}