@iebh/tera-fy 1.15.9 → 2.0.0

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/plugins/vue3.js CHANGED
@@ -1,14 +1,13 @@
1
- import {cloneDeep, debounce} from 'lodash-es';
2
- import TeraFyPluginBase from './base.js';
3
- import {reactive, watch} from 'vue';
1
+ import {cloneDeep} from 'lodash-es';
2
+ import TeraFyPluginFirebase from './firebase.js';
3
+ import {markRaw, reactive as vueReactive, watch as vueWatch} from 'vue';
4
4
 
5
5
  /**
6
6
  * Vue observables plugin
7
- * Provides the `bindProjectState()` function for Vue based projects
8
7
  *
9
8
  * This function is expected to be included via the `terafy.use(MODULE, OPTIONS)` syntax rather than directly
10
9
  *
11
- * @class TeraFyPluginVue
10
+ * @class TeraFyPluginVue3
12
11
  *
13
12
  * @example Implementation within a Vue3 / Vite project within `src/main.js`:
14
13
  * import TeraFy from '@iebh/tera-fy';
@@ -23,125 +22,51 @@ import {reactive, watch} from 'vue';
23
22
  * app.use(terafy.vuePlugin({
24
23
  * globalName: '$tera', // Install as vm.$tera into every component
25
24
  * }));
25
+ *
26
+ * @example Accessing project state - within a Vue component
27
+ * this.$tera.active
26
28
  */
27
- export default class TeraFyPluginVue extends TeraFyPluginBase {
29
+ export default class TeraFyPluginVue3 extends TeraFyPluginFirebase {
28
30
 
29
31
  /**
30
- * Return a Vue reactive object that can be read/written which whose changes will transparently be written back to the TERA server instance
31
- *
32
- * @param {Object} [options] Additional options to mutate behaviour
33
- * @param {Boolean} [options.autoRequire=true] Run `requireProject()` automatically before continuing
34
- * @param {Boolean} [options.read=true] Allow remote reactivity - update the local state when the server changes
35
- * @param {Boolean} [options.write=true] Allow local reactivity to writes - send these to the server
36
- * @param {Object} [options.throttle] Lodash debounce options + `wait` key used to throttle all writes, set to falsy to disable
32
+ * The bound, reactive state of the active TERA project
37
33
  *
38
- * @returns {Promie<Reactive<Object>>} A reactive object representing the project state
39
- */
40
- bindProjectState(options) {
41
- let settings = {
42
- autoRequire: true,
43
- read: true,
44
- write: true,
45
- throttle: {
46
- wait: 200,
47
- maxWait: 2000,
48
- leading: false,
49
- trailing: true,
50
- },
51
- ...options,
52
- };
53
-
54
- return Promise.resolve()
55
- .then(()=> this.getProjectState({
56
- autoRequire: settings.autoRequire ,
57
- }))
58
- .then(snapshot => {
59
- // Create initial reactive
60
- let stateReactive = reactive(snapshot);
61
-
62
- // Watch for remote changes and update
63
- if (settings.read) {
64
- this.events.on(`update:projects/${stateReactive.id}`, newState => {
65
- if (
66
- newState?.lastPatch?.session // Last state change had a session worth noting
67
- && newState.lastPatch.session == this.settings.session // The last state update was made FROM INSIDE THE BUILDING! BUWHAHAHA!
68
- )
69
- return; // Discard it, we don't care
70
-
71
- // Everything else - patch the remote state locally
72
- Object.assign(stateReactive, newState);
73
- });
74
- }
75
-
76
- // Watch for local writes and react
77
- if (settings.write) {
78
-
79
- // NOTE: The below watch function returns two copies of the new value of the observed
80
- // so we have to keep track of what changed ourselves by initalizing against the
81
- // snapshot
82
- let oldVal = cloneDeep(snapshot);
83
-
84
- // Function to handle the state update (can be debounced)
85
- let watchHandle = ()=> {
86
- let newVal = cloneDeep(snapshot);
87
-
88
- this.debug('INFO', 5, 'Update Vue3 Local->Remote', {new: newVal, old: oldVal});
89
- this.createProjectStatePatch(newVal, oldVal);
90
-
91
- // Set oldVal to the deep clone we just made so we can track the diff
92
- oldVal = newVal;
93
- };
94
-
95
- watch(
96
- stateReactive, // State to watch
97
- settings.throttle // Pointer to watchHandle which takes the new state (optionally throttled)
98
- ? debounce(watchHandle, settings.throttle.wait, settings.throttle)
99
- : watchHandle,
100
- { // Watch options
101
- deep: true,
102
- },
103
- );
104
- }
105
-
106
- // Return Vue Reactive
107
- return stateReactive;
108
- })
109
- }
110
-
111
-
112
- /**
113
- * List of available projects for the current session
114
- * @type {VueReactive<Array<Object>>}
115
- */
116
- projects = reactive([]);
117
-
118
-
119
- /**
120
- * The bound, reactive state of a Vue project
121
- * When loaded this represents the state of a project as an object
122
34
  * @type {Object}
123
35
  */
124
- state = null;
36
+ project = null;
125
37
 
126
38
 
127
39
  /**
128
- * Promise used when binding to state
129
- * @type {Promise}
40
+ * Init the project including create a reactive mount for the active project
41
+ *
42
+ * @param {Object} options Additional options to mutate behaviour
43
+ * @param {*...} [options...] see TeraFyPluginFirebase
130
44
  */
131
- statePromisable = null;
45
+ async init(options) {
46
+ await super.init(options); // Initalize parent class Firebase functionality
47
+
48
+ // Mount the project namespace
49
+ this.project = await this.mountNamespace('_PROJECT');
50
+ }
132
51
 
133
52
 
134
- /**
135
- * Utility function which returns an awaitable promise when the state is loading or being refreshed
136
- * This is used in place of `statePromisable` as it has a slightly more logical syntax as a function
137
- *
138
- * @returns {Promise} A promise representing the loading of the project state
139
- *
140
- * @example Await the state loading
141
- * await $tera.statePromise();
142
- */
143
- statePromise() {
144
- return this.statePromisable;
53
+ /** @override */
54
+ getReactive(value) {
55
+ let doc = vueReactive(value);
56
+ return {
57
+ doc,
58
+ setState(state) {
59
+ // Shallow copy all sub-keys into existing object (keeping the object pointer)
60
+ Object.entries(state || {})
61
+ .forEach(([k, v]) => doc[k] = v)
62
+ },
63
+ getState() {
64
+ return cloneDeep(doc);
65
+ },
66
+ watch(cb) {
67
+ vueWatch(doc, cb, {deep: true});
68
+ },
69
+ };
145
70
  }
146
71
 
147
72
 
@@ -161,43 +86,14 @@ export default class TeraFyPluginVue extends TeraFyPluginBase {
161
86
  * @param {VueApp} app The Vue top-level app to install against
162
87
  *
163
88
  * @param {Object} [options] Additional options to mutate behaviour
164
- * @param {Boolean} [options.autoInit=true] Call Init() during the `statePromiseable` cycle if its not already been called
165
- * @param {String} [options.globalName='$tera'] Globa property to allocate this service as
166
- * @param {Objecct} [options.bindOptions] Options passed to `bindProjectState()`
89
+ * @param {String} [options.globalName='$tera'] Global property to allocate this service as
167
90
  */
168
91
  install(app, options) {
169
92
  let settings = {
170
- autoInit: true,
171
93
  globalName: '$tera',
172
- subscribeState: true,
173
- subscribeProjects: true,
174
- stateOptions: {
175
- write: true,
176
- },
177
94
  ...options,
178
95
  };
179
96
 
180
- // Bind $tera.state to the active project
181
- // Initialize state to null
182
- $tera.state = null;
183
-
184
- // $tera.statePromisable becomes the promise we are waiting on to resolve
185
- $tera.statePromisable = Promise.resolve()
186
- .then(()=> settings.autoInit && $tera.init())
187
- .then(()=> Promise.all([
188
- // Bind available project and wait on it
189
- settings.subscribeState && $tera.bindProjectState(settings.stateOptions)
190
- .then(state => $tera.state = state)
191
- .then(()=> $tera.debug('INFO', 1, 'Loaded initial project state', $tera.state)),
192
-
193
- // Fetch available projects
194
- settings.subscribeProjects && $tera.getProjects()
195
- .then(projects => $tera.projects = reactive(projects))
196
- .then(()=> $tera.debug('INFO', 2, 'Loaded project list', $tera.projects)),
197
- ]))
198
- .then(()=> $tera.debug('INFO', 1, 'Ready'))
199
-
200
-
201
97
  // Make this module available globally
202
98
  app.config.globalProperties[settings.globalName] = $tera;
203
99
  },