Skip to content

Commit 922de8c

Browse files
committed
refactor(database): options file
1 parent 7f7cea3 commit 922de8c

File tree

2 files changed

+192
-176
lines changed

2 files changed

+192
-176
lines changed

src/database/index.ts

Lines changed: 4 additions & 176 deletions
Original file line numberDiff line numberDiff line change
@@ -20,23 +20,17 @@ import {
2020
} from 'vue-demi'
2121
import type { DatabaseReference, DataSnapshot, Query } from 'firebase/database'
2222
import { _RefWithState } from '../shared'
23+
import { rtdbUnbinds } from './optionsApi'
2324

24-
/**
25-
* Returns the original reference of a Firebase reference or query across SDK versions.
26-
*
27-
* @param refOrQuery
28-
*/
29-
function getRef(refOrQuery: DatabaseReference | Query): DatabaseReference {
30-
return refOrQuery.ref
31-
}
25+
export { rtdbPlugin } from './optionsApi'
3226

3327
const ops: OperationsType = {
3428
set: (target, key, value) => walkSet(target, key, value),
3529
add: (array, index, data) => array.splice(index, 0, data),
3630
remove: (array, index) => array.splice(index, 1),
3731
}
3832

39-
function internalBind(
33+
export function internalBind(
4034
target: Ref<any>,
4135
key: string,
4236
source: Query | DatabaseReference,
@@ -72,7 +66,7 @@ function internalBind(
7266
})
7367
}
7468

75-
function internalUnbind(
69+
export function internalUnbind(
7670
key: string,
7771
unbinds:
7872
| Record<string, ReturnType<typeof bindAsArray | typeof bindAsObject>>
@@ -88,172 +82,6 @@ function internalUnbind(
8882
// delete vm._firebaseUnbinds[key]
8983
}
9084

91-
interface PluginOptions {
92-
bindName?: string
93-
unbindName?: string
94-
serialize?: RTDBOptions['serialize']
95-
reset?: RTDBOptions['reset']
96-
wait?: RTDBOptions['wait']
97-
}
98-
99-
const defaultOptions: Readonly<Required<PluginOptions>> = {
100-
bindName: '$rtdbBind',
101-
unbindName: '$rtdbUnbind',
102-
serialize: rtdbOptions.serialize,
103-
reset: rtdbOptions.reset,
104-
wait: rtdbOptions.wait,
105-
}
106-
107-
declare module '@vue/runtime-core' {
108-
export interface ComponentCustomProperties {
109-
/**
110-
* Binds a reference
111-
*
112-
* @param name
113-
* @param reference
114-
* @param options
115-
*/
116-
$rtdbBind(
117-
name: string,
118-
reference: DatabaseReference | Query,
119-
options?: RTDBOptions
120-
): Promise<DataSnapshot>
121-
122-
/**
123-
* Unbinds a bound reference
124-
*/
125-
$rtdbUnbind: (name: string, reset?: RTDBOptions['reset']) => void
126-
127-
/**
128-
* Bound firestore references
129-
*/
130-
$firebaseRefs: Readonly<Record<string, DatabaseReference>>
131-
// _firebaseSources: Readonly<
132-
// Record<string, Reference | Query>
133-
// >
134-
/**
135-
* Existing unbind functions that get automatically called when the component is unmounted
136-
* @internal
137-
*/
138-
// _firebaseUnbinds: Readonly<
139-
// Record<string, ReturnType<typeof bindAsArray | typeof bindAsObject>>
140-
// >
141-
}
142-
export interface ComponentCustomOptions {
143-
/**
144-
* Calls `$bind` at created
145-
*/
146-
firebase?: FirebaseOption
147-
}
148-
}
149-
150-
type VueFirebaseObject = Record<string, Query | DatabaseReference>
151-
type FirebaseOption = VueFirebaseObject | (() => VueFirebaseObject)
152-
153-
const rtdbUnbinds = new WeakMap<
154-
object,
155-
Record<string, ReturnType<typeof bindAsArray | typeof bindAsObject>>
156-
>()
157-
158-
/**
159-
* Install this plugin if you want to add `$bind` and `$unbind` functions. Note
160-
* this plugin is not necessary if you exclusively use the Composition API.
161-
*
162-
* @param app
163-
* @param pluginOptions
164-
*/
165-
export function rtdbPlugin(
166-
app: App,
167-
pluginOptions: PluginOptions = defaultOptions
168-
) {
169-
// TODO: implement
170-
// const strategies = Vue.config.optionMergeStrategies
171-
// strategies.firebase = strategies.provide
172-
173-
const globalOptions = Object.assign({}, defaultOptions, pluginOptions)
174-
const { bindName, unbindName } = globalOptions
175-
176-
const GlobalTarget = isVue3
177-
? app.config.globalProperties
178-
: (app as any).prototype
179-
180-
GlobalTarget[unbindName] = function rtdbUnbind(
181-
key: string,
182-
reset?: RTDBOptions['reset']
183-
) {
184-
internalUnbind(key, rtdbUnbinds.get(this), reset)
185-
delete this.$firebaseRefs[key]
186-
}
187-
188-
// add $rtdbBind and $rtdbUnbind methods
189-
GlobalTarget[bindName] = function rtdbBind(
190-
this: ComponentPublicInstance,
191-
key: string,
192-
source: DatabaseReference | Query,
193-
userOptions?: RTDBOptions
194-
) {
195-
const options = Object.assign({}, globalOptions, userOptions)
196-
const target = toRef(this.$data as any, key)
197-
let unbinds = rtdbUnbinds.get(this)
198-
199-
if (unbinds) {
200-
if (unbinds[key]) {
201-
unbinds[key](
202-
// if wait, allow overriding with a function or reset, otherwise, force reset to false
203-
// else pass the reset option
204-
options.wait
205-
? typeof options.reset === 'function'
206-
? options.reset
207-
: false
208-
: options.reset
209-
)
210-
}
211-
} else {
212-
rtdbUnbinds.set(this, (unbinds = {}))
213-
}
214-
215-
const promise = internalBind(target, key, source, unbinds!, options)
216-
217-
// TODO:
218-
// @ts-ignore
219-
// this._firebaseSources[key] = source
220-
this.$firebaseRefs[key] = getRef(source)
221-
222-
return promise
223-
}
224-
225-
// handle firebase option
226-
app.mixin({
227-
beforeCreate(this: ComponentPublicInstance) {
228-
this.$firebaseRefs = Object.create(null)
229-
},
230-
created(this: ComponentPublicInstance) {
231-
let bindings = this.$options.firebase
232-
if (typeof bindings === 'function')
233-
bindings =
234-
// @ts-ignore
235-
bindings.call(this)
236-
if (!bindings) return
237-
238-
for (const key in bindings) {
239-
// @ts-ignore
240-
this[bindName](key, bindings[key], globalOptions)
241-
}
242-
},
243-
244-
beforeUnmount(this: ComponentPublicInstance) {
245-
const unbinds = rtdbUnbinds.get(this)
246-
if (unbinds) {
247-
for (const key in unbinds) {
248-
unbinds[key]()
249-
}
250-
}
251-
// @ts-ignore
252-
this.$firebaseRefs = null
253-
},
254-
})
255-
}
256-
25785
export function bind(
25886
target: Ref,
25987
reference: DatabaseReference | Query,

src/database/optionsApi.ts

Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
import { DatabaseReference, DataSnapshot, Query } from 'firebase/database'
2+
import { App, ComponentPublicInstance, toRef } from 'vue'
3+
import { isVue3 } from 'vue-demi'
4+
import {
5+
rtdbOptions,
6+
RTDBOptions,
7+
rtdbBindAsArray as bindAsArray,
8+
rtdbBindAsObject as bindAsObject,
9+
} from '../core'
10+
import { internalBind, internalUnbind } from './index'
11+
12+
/**
13+
* Returns the original reference of a Firebase reference or query across SDK versions.
14+
*
15+
* @param refOrQuery
16+
*/
17+
function getRef(refOrQuery: DatabaseReference | Query): DatabaseReference {
18+
return refOrQuery.ref
19+
}
20+
21+
interface PluginOptions {
22+
bindName?: string
23+
unbindName?: string
24+
serialize?: RTDBOptions['serialize']
25+
reset?: RTDBOptions['reset']
26+
wait?: RTDBOptions['wait']
27+
}
28+
29+
const defaultOptions: Readonly<Required<PluginOptions>> = {
30+
bindName: '$rtdbBind',
31+
unbindName: '$rtdbUnbind',
32+
serialize: rtdbOptions.serialize,
33+
reset: rtdbOptions.reset,
34+
wait: rtdbOptions.wait,
35+
}
36+
37+
declare module '@vue/runtime-core' {
38+
export interface ComponentCustomProperties {
39+
/**
40+
* Binds a reference
41+
*
42+
* @param name
43+
* @param reference
44+
* @param options
45+
*/
46+
$rtdbBind(
47+
name: string,
48+
reference: DatabaseReference | Query,
49+
options?: RTDBOptions
50+
): Promise<DataSnapshot>
51+
52+
/**
53+
* Unbinds a bound reference
54+
*/
55+
$rtdbUnbind: (name: string, reset?: RTDBOptions['reset']) => void
56+
57+
/**
58+
* Bound firestore references
59+
*/
60+
$firebaseRefs: Readonly<Record<string, DatabaseReference>>
61+
// _firebaseSources: Readonly<
62+
// Record<string, Reference | Query>
63+
// >
64+
/**
65+
* Existing unbind functions that get automatically called when the component is unmounted
66+
* @internal
67+
*/
68+
// _firebaseUnbinds: Readonly<
69+
// Record<string, ReturnType<typeof bindAsArray | typeof bindAsObject>>
70+
// >
71+
}
72+
export interface ComponentCustomOptions {
73+
/**
74+
* Calls `$bind` at created
75+
*/
76+
firebase?: FirebaseOption
77+
}
78+
}
79+
80+
type VueFirebaseObject = Record<string, Query | DatabaseReference>
81+
type FirebaseOption = VueFirebaseObject | (() => VueFirebaseObject)
82+
83+
export const rtdbUnbinds = new WeakMap<
84+
object,
85+
Record<string, ReturnType<typeof bindAsArray | typeof bindAsObject>>
86+
>()
87+
88+
/**
89+
* Install this plugin if you want to add `$bind` and `$unbind` functions. Note
90+
* this plugin is not necessary if you exclusively use the Composition API.
91+
*
92+
* @param app
93+
* @param pluginOptions
94+
*/
95+
export function rtdbPlugin(
96+
app: App,
97+
pluginOptions: PluginOptions = defaultOptions
98+
) {
99+
// TODO: implement
100+
// const strategies = Vue.config.optionMergeStrategies
101+
// strategies.firebase = strategies.provide
102+
103+
const globalOptions = Object.assign({}, defaultOptions, pluginOptions)
104+
const { bindName, unbindName } = globalOptions
105+
106+
const GlobalTarget = isVue3
107+
? app.config.globalProperties
108+
: (app as any).prototype
109+
110+
GlobalTarget[unbindName] = function rtdbUnbind(
111+
key: string,
112+
reset?: RTDBOptions['reset']
113+
) {
114+
internalUnbind(key, rtdbUnbinds.get(this), reset)
115+
delete this.$firebaseRefs[key]
116+
}
117+
118+
// add $rtdbBind and $rtdbUnbind methods
119+
GlobalTarget[bindName] = function rtdbBind(
120+
this: ComponentPublicInstance,
121+
key: string,
122+
source: DatabaseReference | Query,
123+
userOptions?: RTDBOptions
124+
) {
125+
const options = Object.assign({}, globalOptions, userOptions)
126+
const target = toRef(this.$data as any, key)
127+
let unbinds = rtdbUnbinds.get(this)
128+
129+
if (unbinds) {
130+
if (unbinds[key]) {
131+
unbinds[key](
132+
// if wait, allow overriding with a function or reset, otherwise, force reset to false
133+
// else pass the reset option
134+
options.wait
135+
? typeof options.reset === 'function'
136+
? options.reset
137+
: false
138+
: options.reset
139+
)
140+
}
141+
} else {
142+
rtdbUnbinds.set(this, (unbinds = {}))
143+
}
144+
145+
const promise = internalBind(target, key, source, unbinds!, options)
146+
147+
// TODO:
148+
// this._firebaseSources[key] = source
149+
// we make it readonly for the user but we must change it. Maybe there is a way to have an internal type here but expose a readonly type through a d.ts
150+
;(this.$firebaseRefs as Mutable<Record<string, DatabaseReference>>)[key] =
151+
getRef(source)
152+
153+
return promise
154+
}
155+
156+
// handle firebase option
157+
app.mixin({
158+
beforeCreate(this: ComponentPublicInstance) {
159+
this.$firebaseRefs = Object.create(null)
160+
},
161+
created(this: ComponentPublicInstance) {
162+
let bindings = this.$options.firebase
163+
if (typeof bindings === 'function')
164+
bindings =
165+
// @ts-ignore
166+
bindings.call(this)
167+
if (!bindings) return
168+
169+
for (const key in bindings) {
170+
// @ts-ignore
171+
this[bindName](key, bindings[key], globalOptions)
172+
}
173+
},
174+
175+
beforeUnmount(this: ComponentPublicInstance) {
176+
const unbinds = rtdbUnbinds.get(this)
177+
if (unbinds) {
178+
for (const key in unbinds) {
179+
unbinds[key]()
180+
}
181+
}
182+
// @ts-ignore
183+
this.$firebaseRefs = null
184+
},
185+
})
186+
}
187+
188+
type Mutable<T> = { -readonly [P in keyof T]: T[P] }

0 commit comments

Comments
 (0)