1 | import angular from './angularCompatibility.js';
|
2 | import Vue from 'vue';
|
3 | import Reference from './Reference.js';
|
4 |
|
5 |
|
6 | export 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 |
|
58 |
|
59 |
|
60 | this._auth.serial++;
|
61 | return this._handleAuthChange(null).then(approved => {
|
62 |
|
63 |
|
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 | }
|