@edgedev/firebase 1.1.6 → 1.2.2

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/README.md CHANGED
@@ -16,23 +16,80 @@ pnpm install @edgedev/firebase
16
16
  ```bash
17
17
  pnpm install @edgedev/firebase
18
18
  ```
19
- If installing into a Nuxt 3 project, you can make this globally avaiable by adding a file (whatever.ts) to your "composables" folder with the code below.
19
+ ### Installing with Nuxt 3 global composables
20
+
21
+ Add a file (whatever.ts) to your "composables" folder with this code:
20
22
 
21
23
  ```typescript
22
- import * as edgeFirebase from "@edgedev/firebase";
24
+ import { EdgeFirebase } from "@edgedev/firebase";
25
+ const config = {
26
+ apiKey: "your-apiKey",
27
+ authDomain: "your-authDomain",
28
+ projectId: "your-projectId",
29
+ storageBucket: "your-storageBucket",
30
+ messagingSenderId: "your-messagingSenderId",
31
+ appId: "your-appId"
32
+ };
33
+ const edgeFirebase = new EdgeFirebase(config);
23
34
  export { edgeFirebase };
24
35
  ```
25
36
 
26
- Also if on Nuxt 3 - SSR must be disabled, update the nuxt.config.ts file:
37
+ ##### *Nuxt must be configured with SSR disabled, update the nuxt.config.ts file (if other parts of your project SSR, see Nuxt 3 plugin instuctions):
38
+ ```javascript
39
+ export default defineNuxtConfig({ ssr: false });
40
+ ```
41
+
42
+ ### Installing as a plugin
43
+
44
+ #### Vue 3 plugin, main.js example:
45
+ ```javascript
46
+ import { createApp } from "vue";
47
+ import App from "./App.vue";
48
+
49
+ //edgeFirebase Plugin
50
+ import eFb from "@edgedev/firebase";
51
+ app.use(eFb, {
52
+ apiKey: "your-apiKey",
53
+ authDomain: "your-authDomain",
54
+ projectId: "your-projectId",
55
+ storageBucket: "your-storageBucket",
56
+ messagingSenderId: "your-messagingSenderId",
57
+ appId: "your-appId"
58
+ })
59
+ //end edgeFirebase
60
+
61
+ app.mount("#app");
62
+ ```
63
+
64
+ #### Nuxt 3 example using the plugins folder:
65
+ Add a file (whatever**.client**.ts) to your "plugins" folder with the following code:
66
+
67
+ ***-Note the ".client" in the file name. If the file doesn't have that in the name you must disabled SSR in the nuxt config.***
68
+ ```javascript
69
+ import eFb from "@edgedev/firebase";
70
+ export default defineNuxtPlugin((nuxtApp) => {
71
+ nuxtApp.vueApp.use(eFb, {
72
+ apiKey: "your-apiKey",
73
+ authDomain: "your-authDomain",
74
+ projectId: "your-projectId",
75
+ storageBucket: "your-storageBucket",
76
+ messagingSenderId: "your-messagingSenderId",
77
+ appId: "your-appId"
78
+ });
79
+ });
80
+ ```
81
+ ***-Alternatively you can disable SSR for your entire Nuxt project instead of naming the plugin with ".client", update the nuxt.config.ts file:***
82
+
27
83
  ```javascript
28
84
  export default defineNuxtConfig({ ssr: false });
29
85
  ```
30
86
 
31
- If not in Nuxt 3 or there is no need for it to be global, put this in your components <script setup>
32
87
 
88
+ #### After installing as a plugin you will need to include this in <script setup> in any component you want to use EdgeFirebase in:
33
89
  ```javascript
34
90
  <script setup>
35
- import * as edgeFirebase from "@edgedev/firebase";
91
+ import { inject } from "vue";
92
+ const edgeFirebase = inject("edgeFirebase");
36
93
  </script>
37
94
  ```
38
95
  # Firebase Authentication
@@ -0,0 +1,485 @@
1
+ import { initializeApp } from "firebase/app";
2
+ import { reactive } from "vue";
3
+
4
+ import {
5
+ getFirestore,
6
+ collection,
7
+ addDoc,
8
+ doc,
9
+ query,
10
+ onSnapshot,
11
+ WhereFilterOp,
12
+ QueryConstraint,
13
+ Unsubscribe,
14
+ where,
15
+ deleteDoc,
16
+ getDocs,
17
+ getDoc,
18
+ orderBy,
19
+ limit,
20
+ Query,
21
+ startAfter,
22
+ DocumentData,
23
+ setDoc
24
+ } from "firebase/firestore";
25
+
26
+ import {
27
+ getAuth,
28
+ setPersistence,
29
+ browserSessionPersistence,
30
+ browserLocalPersistence,
31
+ Persistence,
32
+ signInWithEmailAndPassword,
33
+ onAuthStateChanged,
34
+ signOut
35
+ } from "firebase/auth";
36
+
37
+ interface FirestoreQuery {
38
+ field: string;
39
+ operator: WhereFilterOp; // '==' | '<' | '<=' | '>' | '>=' | 'array-contains' | 'in' | 'array-contains-any';
40
+ value: unknown;
41
+ }
42
+
43
+ interface FirestoreOrderBy {
44
+ field: string;
45
+ direction: "asc" | "desc";
46
+ }
47
+
48
+ interface FirestoreLimit {
49
+ limit: number;
50
+ }
51
+
52
+ interface CollectionUnsubscribeObject {
53
+ [key: string]: Unsubscribe;
54
+ }
55
+
56
+ interface CollectionDataObject {
57
+ [key: string]: object;
58
+ }
59
+
60
+ interface UserDataObject {
61
+ uid: string | null;
62
+ email: string;
63
+ loggedIn: boolean;
64
+ logInError: boolean;
65
+ logInErrorMessage: string;
66
+ }
67
+
68
+ interface Credentials {
69
+ email: string;
70
+ password: string;
71
+ }
72
+
73
+ interface StaticDataResult {
74
+ data: object;
75
+ next: DocumentData | null;
76
+ }
77
+
78
+ interface firebaseConfig {
79
+ apiKey: string;
80
+ authDomain: string;
81
+ projectId: string;
82
+ storageBucket: string;
83
+ messagingSenderId: string;
84
+ appId: string;
85
+ }
86
+
87
+ export const EdgeFirebase = class {
88
+ constructor(
89
+ firebaseConfig: firebaseConfig = {
90
+ apiKey: "",
91
+ authDomain: "",
92
+ projectId: "",
93
+ storageBucket: "",
94
+ messagingSenderId: "",
95
+ appId: ""
96
+ }
97
+ ) {
98
+ this.firebaseConfig = firebaseConfig;
99
+ this.app = initializeApp(this.firebaseConfig);
100
+ this.auth = getAuth(this.app);
101
+ this.db = getFirestore(this.app);
102
+ this.setOnAuthStateChanged();
103
+ }
104
+
105
+ private firebaseConfig = null;
106
+
107
+ // Initialize Firebase
108
+ public app = null;
109
+ public auth = null;
110
+ public db = null;
111
+
112
+ // Composable to logout
113
+ public logOut = (): void => {
114
+ signOut(this.auth)
115
+ .then(() => {
116
+ Object.keys(this.unsubscibe).forEach((key) => {
117
+ if (this.unsubscibe[key] instanceof Function) {
118
+ this.unsubscibe[key]();
119
+ this.unsubscibe[key] = null;
120
+ }
121
+ });
122
+ })
123
+ .catch(() => {
124
+ // Do nothing
125
+ });
126
+ };
127
+
128
+ private setOnAuthStateChanged = (): void => {
129
+ onAuthStateChanged(this.auth, (userAuth) => {
130
+ if (userAuth) {
131
+ this.user.email = userAuth.email;
132
+ this.user.uid = userAuth.uid;
133
+ this.user.loggedIn = true;
134
+ this.user.logInError = false;
135
+ this.user.logInErrorMessage = "";
136
+ } else {
137
+ this.user.email = "";
138
+ this.user.uid = null;
139
+ this.user.loggedIn = false;
140
+ this.user.logInError = false;
141
+ this.user.logInErrorMessage = "";
142
+ }
143
+ });
144
+ };
145
+
146
+ // Composable to login and set persistence
147
+ public logIn = (credentials: Credentials, isPersistant = false): void => {
148
+ this.logOut();
149
+ let persistence: Persistence = browserSessionPersistence;
150
+ if (isPersistant) {
151
+ persistence = browserLocalPersistence;
152
+ }
153
+ setPersistence(this.auth, persistence)
154
+ .then(() => {
155
+ signInWithEmailAndPassword(
156
+ this.auth,
157
+ credentials.email,
158
+ credentials.password
159
+ )
160
+ .then(() => {
161
+ // do nothing
162
+ })
163
+ .catch((error) => {
164
+ this.user.email = "";
165
+ this.user.uid = null;
166
+
167
+ this.user.loggedIn = false;
168
+ this.user.logInError = true;
169
+ this.user.logInErrorMessage = error.code + ": " + error.message;
170
+ });
171
+ })
172
+ .catch((error) => {
173
+ this.user.email = "";
174
+ this.user.uid = null;
175
+
176
+ this.user.loggedIn = false;
177
+ this.user.logInError = true;
178
+ this.user.logInErrorMessage = error.code + ": " + error.message;
179
+ });
180
+ };
181
+
182
+ // Keeping this for reference on how to Type a Ref.
183
+ // const user = ref<UserDataObject>({
184
+ // uid: null,
185
+ // email: "",
186
+ // loggedIn: false,
187
+ // logInError: false,
188
+ // logInErrorMessage: ""
189
+ // });
190
+
191
+ // Simple Store Items (add matching key per firebase collection)
192
+ public data: CollectionDataObject = reactive({});
193
+ public unsubscibe: CollectionUnsubscribeObject = reactive({});
194
+ public user: UserDataObject = reactive({
195
+ uid: null,
196
+ email: "",
197
+ loggedIn: false,
198
+ logInError: false,
199
+ logInErrorMessage: ""
200
+ });
201
+
202
+ public getDocData = async (
203
+ collectionPath: string,
204
+ docId: string
205
+ ): Promise<{ [key: string]: unknown }> => {
206
+ const docRef = doc(this.db, collectionPath, docId);
207
+ const docSnap = await getDoc(docRef);
208
+ const docData = docSnap.data();
209
+ docData.docId = docSnap.id;
210
+ return docData;
211
+ };
212
+
213
+ private getStaticData = async (
214
+ collectionPath: string,
215
+ queryList: FirestoreQuery[] = [],
216
+ orderList: FirestoreOrderBy[] = [],
217
+ max = 0,
218
+ last: DocumentData | null = null
219
+ ): Promise<StaticDataResult> => {
220
+ const data: object = {};
221
+
222
+ const q = this.getQuery(collectionPath, queryList, orderList, max, last);
223
+
224
+ const docs = await getDocs(q);
225
+ const nextLast: DocumentData = docs.docs[docs.docs.length - 1];
226
+
227
+ docs.forEach((doc) => {
228
+ const item = doc.data();
229
+ item.docId = doc.id;
230
+ data[doc.id] = item;
231
+ });
232
+ return { data, next: nextLast };
233
+ };
234
+
235
+ // Class for wrapping a getSaticData to handle pagination
236
+ get SearchStaticData() {
237
+ const getStaticData = this.getStaticData;
238
+ return class {
239
+ private collectionPath = "";
240
+ private queryList: FirestoreQuery[] = [];
241
+ private orderList: FirestoreOrderBy[] = [];
242
+ private max = 0;
243
+
244
+ public results = reactive({
245
+ data: {},
246
+ pagination: [],
247
+ staticIsLastPage: true,
248
+ staticIsFirstPage: true,
249
+ staticCurrentPage: ""
250
+ });
251
+
252
+ public prev = async (): Promise<void> => {
253
+ const findIndex = this.results.pagination.findIndex(
254
+ (x) => x.key === this.results.staticCurrentPage
255
+ );
256
+ let last = null;
257
+ if (findIndex === 1) {
258
+ this.results.staticCurrentPage = "";
259
+ this.results.staticIsLastPage = false;
260
+ this.results.staticIsFirstPage = true;
261
+ } else {
262
+ last = this.results.pagination[findIndex - 2].next;
263
+ this.results.staticCurrentPage =
264
+ this.results.pagination[findIndex - 2].key;
265
+ }
266
+ await this.afterNextPrev(last);
267
+ };
268
+
269
+ public next = async (): Promise<void> => {
270
+ const findIndex = this.results.pagination.findIndex(
271
+ (x) => x.key === this.results.staticCurrentPage
272
+ );
273
+ const last = this.results.pagination[findIndex].next;
274
+ if (this.results.pagination.length === 1) {
275
+ this.results.staticIsFirstPage = true;
276
+ } else {
277
+ this.results.staticIsFirstPage = false;
278
+ }
279
+ await this.afterNextPrev(last);
280
+ };
281
+
282
+ private afterNextPrev = async (last): Promise<void> => {
283
+ let results = await getStaticData(
284
+ "users",
285
+ this.queryList,
286
+ this.orderList,
287
+ this.max,
288
+ last
289
+ );
290
+
291
+ if (last && Object.keys(results.data).length === 0) {
292
+ this.results.staticIsLastPage = true;
293
+ if (this.results.pagination.length === 1) {
294
+ last = null;
295
+ this.results.staticCurrentPage = "";
296
+ this.results.staticIsFirstPage = true;
297
+ } else {
298
+ last =
299
+ this.results.pagination[this.results.pagination.length - 2].next;
300
+ this.results.staticCurrentPage =
301
+ this.results.pagination[this.results.pagination.length - 2].key;
302
+ }
303
+ results = await getStaticData(
304
+ "users",
305
+ this.queryList,
306
+ this.orderList,
307
+ this.max,
308
+ last
309
+ );
310
+ } else {
311
+ this.results.staticIsLastPage = false;
312
+ if (this.results.pagination.length === 1) {
313
+ this.results.staticIsFirstPage = false;
314
+ }
315
+ }
316
+ this.results.data = results.data;
317
+ this.results.staticCurrentPage = results.next.id;
318
+ if (!this.results.staticIsLastPage) {
319
+ if (results.next) {
320
+ const findItem = this.results.pagination.find(
321
+ (x) => x.key === results.next.id
322
+ );
323
+ if (!findItem) {
324
+ this.results.pagination.push({
325
+ key: results.next.id,
326
+ next: results.next
327
+ });
328
+ }
329
+ }
330
+ }
331
+ };
332
+
333
+ public getData = async (
334
+ collectionPath: string,
335
+ queryList: FirestoreQuery[] = [],
336
+ orderList: FirestoreOrderBy[] = [],
337
+ max = 0
338
+ ): Promise<void> => {
339
+ this.collectionPath = collectionPath;
340
+ this.queryList = queryList;
341
+ this.orderList = orderList;
342
+ this.max = max;
343
+ this.results.staticIsLastPage = true;
344
+ this.results.staticIsFirstPage = true;
345
+ this.results.staticCurrentPage = "";
346
+ this.results.pagination = [];
347
+ this.results.pagination = [];
348
+ this.results.data = {};
349
+ const results = await getStaticData(
350
+ collectionPath,
351
+ queryList,
352
+ orderList,
353
+ max
354
+ );
355
+ if (Object.keys(results.data).length > 0) {
356
+ this.results.staticIsLastPage = false;
357
+ this.results.data = results.data;
358
+ this.results.staticCurrentPage = results.next.id;
359
+ this.results.pagination.push({
360
+ key: results.next.id,
361
+ next: results.next
362
+ });
363
+ } else {
364
+ this.results.staticIsLastPage = true;
365
+ this.results.staticIsFirstPage = true;
366
+ }
367
+ };
368
+ };
369
+ }
370
+
371
+ // Class for wrapping a getSaticData to handle pagination
372
+ public SearchStaticDatas = new (class {})();
373
+
374
+ // Composable to start snapshot listener and set unsubscribe function
375
+ public startSnapshot = (
376
+ collectionPath: string,
377
+ queryList: FirestoreQuery[] = [],
378
+ orderList: FirestoreOrderBy[] = [],
379
+ max = 0
380
+ ): void => {
381
+ this.data[collectionPath] = {};
382
+ const q = this.getQuery(collectionPath, queryList, orderList, max);
383
+ const unsubscribe = onSnapshot(q, (querySnapshot) => {
384
+ const items = {};
385
+ querySnapshot.forEach((doc) => {
386
+ const item = doc.data();
387
+ item.docId = doc.id;
388
+ items[doc.id] = item;
389
+ });
390
+ this.data[collectionPath] = items;
391
+ });
392
+ this.unsubscibe[collectionPath] = unsubscribe;
393
+ };
394
+
395
+ private getQuery = (
396
+ collectionPath: string,
397
+ queryList: FirestoreQuery[] = [],
398
+ orderList: FirestoreOrderBy[] = [],
399
+ max = 0,
400
+ after: DocumentData | null = null
401
+ ): Query => {
402
+ const queryConditions: QueryConstraint[] = queryList.map((condition) =>
403
+ where(condition.field, condition.operator, condition.value)
404
+ );
405
+
406
+ const orderConditions: QueryConstraint[] = orderList.map((condition) =>
407
+ orderBy(condition.field, condition.direction)
408
+ );
409
+
410
+ let limitList: FirestoreLimit[] = [];
411
+ if (max > 0) {
412
+ limitList = [{ limit: max }];
413
+ }
414
+
415
+ const limitConditions: QueryConstraint[] = limitList.map((condition) =>
416
+ limit(condition.limit)
417
+ );
418
+ if (after) {
419
+ return query(
420
+ collection(this.db, collectionPath),
421
+ ...queryConditions,
422
+ ...orderConditions,
423
+ ...limitConditions,
424
+ startAfter(after)
425
+ );
426
+ }
427
+ return query(
428
+ collection(this.db, collectionPath),
429
+ ...queryConditions,
430
+ ...orderConditions,
431
+ ...limitConditions
432
+ );
433
+ };
434
+
435
+ // Composable to update/add a document
436
+ public storeDoc = async (
437
+ collectionPath: string,
438
+ item: object
439
+ ): Promise<void> => {
440
+ const cloneItem = JSON.parse(JSON.stringify(item));
441
+ const currentTime = new Date().getTime();
442
+ cloneItem.last_updated = currentTime;
443
+ cloneItem.uid = this.user.uid;
444
+ if (!Object.prototype.hasOwnProperty.call(cloneItem, "doc_created_at")) {
445
+ cloneItem.doc_created_at = currentTime;
446
+ }
447
+ if (Object.prototype.hasOwnProperty.call(cloneItem, "docId")) {
448
+ const docId = cloneItem.docId;
449
+ if (Object.prototype.hasOwnProperty.call(this.data, collectionPath)) {
450
+ this.data[collectionPath][docId] = cloneItem;
451
+ }
452
+ setDoc(doc(this.db, collectionPath, docId), cloneItem);
453
+ } else {
454
+ const docRef = await addDoc(
455
+ collection(this.db, collectionPath),
456
+ cloneItem
457
+ );
458
+ if (Object.prototype.hasOwnProperty.call(this.data, collectionPath)) {
459
+ this.data[collectionPath][docRef.id] = cloneItem;
460
+ }
461
+ this.storeDoc(collectionPath, { ...cloneItem, docId: docRef.id });
462
+ }
463
+ };
464
+
465
+ // Composable to delete a document
466
+ public removeDoc = (collectionPath: string, docId: string): void => {
467
+ // Just in case getting collection back from firebase is slow:
468
+ if (Object.prototype.hasOwnProperty.call(this.data, collectionPath)) {
469
+ if (
470
+ Object.prototype.hasOwnProperty.call(this.data[collectionPath], docId)
471
+ ) {
472
+ delete this.data[collectionPath][docId];
473
+ }
474
+ }
475
+ deleteDoc(doc(this.db, collectionPath, docId));
476
+ };
477
+
478
+ // Composable to stop snapshot listener
479
+ public stopSnapshot = (collectionPath: string): void => {
480
+ if (this.unsubscibe[collectionPath] instanceof Function) {
481
+ this.unsubscibe[collectionPath]();
482
+ this.unsubscibe[collectionPath] = null;
483
+ }
484
+ };
485
+ };
package/index.ts CHANGED
@@ -1,448 +1,8 @@
1
- import { initializeApp } from "firebase/app";
2
- import { reactive } from "vue";
3
-
4
- import {
5
- getFirestore,
6
- collection,
7
- addDoc,
8
- updateDoc,
9
- doc,
10
- query,
11
- onSnapshot,
12
- WhereFilterOp,
13
- QueryConstraint,
14
- Unsubscribe,
15
- where,
16
- deleteDoc,
17
- getDocs,
18
- getDoc,
19
- orderBy,
20
- limit,
21
- Query,
22
- startAfter,
23
- DocumentData,
24
- setDoc
25
- } from "firebase/firestore";
26
-
27
- import {
28
- getAuth,
29
- setPersistence,
30
- browserSessionPersistence,
31
- browserLocalPersistence,
32
- Persistence,
33
- signInWithEmailAndPassword,
34
- onAuthStateChanged,
35
- signOut
36
- } from "firebase/auth";
37
-
38
- interface FirestoreQuery {
39
- field: string;
40
- operator: WhereFilterOp; // '==' | '<' | '<=' | '>' | '>=' | 'array-contains' | 'in' | 'array-contains-any';
41
- value: unknown;
42
- }
43
-
44
- interface FirestoreOrderBy {
45
- field: string;
46
- direction: "asc" | "desc";
47
- }
48
-
49
- interface FirestoreLimit {
50
- limit: number;
51
- }
52
-
53
- interface CollectionUnsubscribeObject {
54
- [key: string]: Unsubscribe;
55
- }
56
-
57
- interface CollectionDataObject {
58
- [key: string]: object;
59
- }
60
-
61
- interface UserDataObject {
62
- uid: string | null;
63
- email: string;
64
- loggedIn: boolean;
65
- logInError: boolean;
66
- logInErrorMessage: string;
67
- }
68
-
69
- interface Credentials {
70
- email: string;
71
- password: string;
72
- }
73
-
74
- interface StaticDataResult {
75
- data: object;
76
- next: DocumentData | null;
77
- }
78
-
79
- const firebaseConfig = {
80
- apiKey: import.meta.env.VITE_FIREBASE_API_KEY as string,
81
- authDomain: import.meta.env.VITE_FIREBASE_AUTH_DOMAIN as string,
82
- projectId: import.meta.env.VITE_FIREBASE_PROJECT_ID as string,
83
- storageBucket: import.meta.env.VITE_FIREBASE_STORAGE_BUCKET as string,
84
- messagingSenderId: import.meta.env
85
- .VITE_FIREBASE_MESSAGING_SENDER_ID as string,
86
- appId: import.meta.env.VITE_FIREBASE_APP_ID as string
87
- };
88
-
89
- // Initialize Firebase
90
- export const app = initializeApp(firebaseConfig);
91
- export const auth = getAuth(app);
92
- export const db = getFirestore(app);
93
-
94
- onAuthStateChanged(auth, (userAuth) => {
95
- if (userAuth) {
96
- user.email = userAuth.email;
97
- user.uid = userAuth.uid;
98
- user.loggedIn = true;
99
- user.logInError = false;
100
- user.logInErrorMessage = "";
101
- } else {
102
- user.email = "";
103
- user.uid = null;
104
- user.loggedIn = false;
105
- user.logInError = false;
106
- user.logInErrorMessage = "";
107
- }
108
- });
109
-
110
- // Composable to logout
111
- export const logOut = (): void => {
112
- signOut(auth)
113
- .then(() => {
114
- Object.keys(unsubscibe).forEach((key) => {
115
- if (unsubscibe[key] instanceof Function) {
116
- unsubscibe[key]();
117
- unsubscibe[key] = null;
118
- }
119
- });
120
- })
121
- .catch(() => {
122
- // Do nothing
123
- });
124
- };
125
-
126
- // Composable to login and set persistence
127
- export const logIn = (credentials: Credentials, isPersistant = false): void => {
128
- logOut();
129
- let persistence: Persistence = browserSessionPersistence;
130
- if (isPersistant) {
131
- persistence = browserLocalPersistence;
132
- }
133
- setPersistence(auth, persistence)
134
- .then(() => {
135
- signInWithEmailAndPassword(auth, credentials.email, credentials.password)
136
- .then(() => {
137
- // do nothing
138
- })
139
- .catch((error) => {
140
- user.email = "";
141
- user.uid = null;
142
-
143
- user.loggedIn = false;
144
- user.logInError = true;
145
- user.logInErrorMessage = error.code + ": " + error.message;
146
- });
147
- })
148
- .catch((error) => {
149
- user.email = "";
150
- user.uid = null;
151
-
152
- user.loggedIn = false;
153
- user.logInError = true;
154
- user.logInErrorMessage = error.code + ": " + error.message;
155
- });
156
- };
157
-
158
- // Keeping this for reference on how to Type a Ref.
159
- // export const user = ref<UserDataObject>({
160
- // uid: null,
161
- // email: "",
162
- // loggedIn: false,
163
- // logInError: false,
164
- // logInErrorMessage: ""
165
- // });
166
-
167
- // Simple Store Items (add matching key per firebase collection)
168
- export const data: CollectionDataObject = reactive({});
169
- export const unsubscibe: CollectionUnsubscribeObject = reactive({});
170
- export const user: UserDataObject = reactive({
171
- uid: null,
172
- email: "",
173
- loggedIn: false,
174
- logInError: false,
175
- logInErrorMessage: ""
176
- });
177
-
178
- export const getDocData = async (
179
- collectionPath: string,
180
- docId: string
181
- ): Promise<{ [key: string]: unknown }> => {
182
- const docRef = doc(db, collectionPath, docId);
183
- const docSnap = await getDoc(docRef);
184
- const docData = docSnap.data();
185
- docData.docId = docSnap.id;
186
- return docData;
187
- };
188
-
189
- export const getStaticData = async (
190
- collectionPath: string,
191
- queryList: FirestoreQuery[] = [],
192
- orderList: FirestoreOrderBy[] = [],
193
- max = 0,
194
- last: DocumentData | null = null
195
- ): Promise<StaticDataResult> => {
196
- const data: object = {};
197
-
198
- const q = getQuery(collectionPath, queryList, orderList, max, last);
199
-
200
- const docs = await getDocs(q);
201
- const nextLast: DocumentData = docs.docs[docs.docs.length - 1];
202
-
203
- docs.forEach((doc) => {
204
- const item = doc.data();
205
- item.docId = doc.id;
206
- data[doc.id] = item;
207
- });
208
- return { data, next: nextLast };
209
- };
210
-
211
- // Class for wrapping a getSaticData to handle pagination
212
- export class SearchStaticData {
213
- collectionPath = "";
214
- queryList: FirestoreQuery[] = [];
215
- orderList: FirestoreOrderBy[] = [];
216
- max = 0;
217
-
218
- results = reactive({
219
- data: {},
220
- pagination: [],
221
- staticIsLastPage: true,
222
- staticIsFirstPage: true,
223
- staticCurrentPage: ""
224
- });
225
-
226
- prev = async (): Promise<void> => {
227
- const findIndex = this.results.pagination.findIndex(
228
- (x) => x.key === this.results.staticCurrentPage
229
- );
230
- let last = null;
231
- if (findIndex === 1) {
232
- this.results.staticCurrentPage = "";
233
- this.results.staticIsLastPage = false;
234
- this.results.staticIsFirstPage = true;
235
- } else {
236
- last = this.results.pagination[findIndex - 2].next;
237
- this.results.staticCurrentPage =
238
- this.results.pagination[findIndex - 2].key;
239
- }
240
- await this.afterNextPrev(last);
241
- };
242
-
243
- next = async (): Promise<void> => {
244
- const findIndex = this.results.pagination.findIndex(
245
- (x) => x.key === this.results.staticCurrentPage
246
- );
247
- const last = this.results.pagination[findIndex].next;
248
- if (this.results.pagination.length === 1) {
249
- this.results.staticIsFirstPage = true;
250
- } else {
251
- this.results.staticIsFirstPage = false;
252
- }
253
- await this.afterNextPrev(last);
254
- };
255
-
256
- afterNextPrev = async (last): Promise<void> => {
257
- let results = await getStaticData(
258
- "users",
259
- this.queryList,
260
- this.orderList,
261
- this.max,
262
- last
263
- );
264
-
265
- if (last && Object.keys(results.data).length === 0) {
266
- this.results.staticIsLastPage = true;
267
- if (this.results.pagination.length === 1) {
268
- last = null;
269
- this.results.staticCurrentPage = "";
270
- this.results.staticIsFirstPage = true;
271
- } else {
272
- last = this.results.pagination[this.results.pagination.length - 2].next;
273
- this.results.staticCurrentPage =
274
- this.results.pagination[this.results.pagination.length - 2].key;
275
- }
276
- results = await getStaticData(
277
- "users",
278
- this.queryList,
279
- this.orderList,
280
- this.max,
281
- last
282
- );
283
- } else {
284
- this.results.staticIsLastPage = false;
285
- if (this.results.pagination.length === 1) {
286
- this.results.staticIsFirstPage = false;
287
- }
288
- }
289
- this.results.data = results.data;
290
- this.results.staticCurrentPage = results.next.id;
291
- if (!this.results.staticIsLastPage) {
292
- if (results.next) {
293
- const findItem = this.results.pagination.find(
294
- (x) => x.key === results.next.id
295
- );
296
- if (!findItem) {
297
- this.results.pagination.push({
298
- key: results.next.id,
299
- next: results.next
300
- });
301
- }
302
- }
303
- }
304
- };
305
-
306
- getData = async (
307
- collectionPath: string,
308
- queryList: FirestoreQuery[] = [],
309
- orderList: FirestoreOrderBy[] = [],
310
- max = 0
311
- ): Promise<void> => {
312
- this.collectionPath = collectionPath;
313
- this.queryList = queryList;
314
- this.orderList = orderList;
315
- this.max = max;
316
- this.results.staticIsLastPage = true;
317
- this.results.staticIsFirstPage = true;
318
- this.results.staticCurrentPage = "";
319
- this.results.pagination = [];
320
- this.results.pagination = [];
321
- this.results.data = {};
322
- const results = await getStaticData(
323
- collectionPath,
324
- queryList,
325
- orderList,
326
- max
327
- );
328
- if (Object.keys(results.data).length > 0) {
329
- this.results.staticIsLastPage = false;
330
- this.results.data = results.data;
331
- this.results.staticCurrentPage = results.next.id;
332
- this.results.pagination.push({
333
- key: results.next.id,
334
- next: results.next
335
- });
336
- } else {
337
- this.results.staticIsLastPage = true;
338
- this.results.staticIsFirstPage = true;
339
- }
340
- };
341
- }
342
-
343
- // Composable to start snapshot listener and set unsubscribe function
344
- export const startSnapshot = (
345
- collectionPath: string,
346
- queryList: FirestoreQuery[] = [],
347
- orderList: FirestoreOrderBy[] = [],
348
- max = 0
349
- ): void => {
350
- data[collectionPath] = {};
351
- const q = getQuery(collectionPath, queryList, orderList, max);
352
- const unsubscribe = onSnapshot(q, (querySnapshot) => {
353
- const items = {};
354
- querySnapshot.forEach((doc) => {
355
- const item = doc.data();
356
- item.docId = doc.id;
357
- items[doc.id] = item;
358
- });
359
- data[collectionPath] = items;
360
- });
361
- unsubscibe[collectionPath] = unsubscribe;
362
- };
363
-
364
- const getQuery = (
365
- collectionPath: string,
366
- queryList: FirestoreQuery[] = [],
367
- orderList: FirestoreOrderBy[] = [],
368
- max = 0,
369
- after: DocumentData | null = null
370
- ): Query => {
371
- const queryConditions: QueryConstraint[] = queryList.map((condition) =>
372
- where(condition.field, condition.operator, condition.value)
373
- );
374
-
375
- const orderConditions: QueryConstraint[] = orderList.map((condition) =>
376
- orderBy(condition.field, condition.direction)
377
- );
378
-
379
- let limitList: FirestoreLimit[] = [];
380
- if (max > 0) {
381
- limitList = [{ limit: max }];
382
- }
383
-
384
- const limitConditions: QueryConstraint[] = limitList.map((condition) =>
385
- limit(condition.limit)
386
- );
387
- if (after) {
388
- return query(
389
- collection(db, collectionPath),
390
- ...queryConditions,
391
- ...orderConditions,
392
- ...limitConditions,
393
- startAfter(after)
394
- );
395
- }
396
- return query(
397
- collection(db, collectionPath),
398
- ...queryConditions,
399
- ...orderConditions,
400
- ...limitConditions
401
- );
402
- };
403
-
404
- // Composable to update/add a document
405
- export const storeDoc = async (
406
- collectionPath: string,
407
- item: object
408
- ): Promise<void> => {
409
- const cloneItem = JSON.parse(JSON.stringify(item));
410
- const currentTime = new Date().getTime();
411
- cloneItem.last_updated = currentTime;
412
- cloneItem.uid = user.uid;
413
- if (!Object.prototype.hasOwnProperty.call(cloneItem, "doc_created_at")) {
414
- cloneItem.doc_created_at = currentTime;
415
- }
416
- if (Object.prototype.hasOwnProperty.call(cloneItem, "docId")) {
417
- const docId = cloneItem.docId;
418
- if (Object.prototype.hasOwnProperty.call(data, collectionPath)) {
419
- data[collectionPath][docId] = cloneItem;
420
- }
421
- setDoc(doc(db, collectionPath, docId), cloneItem);
422
- } else {
423
- const docRef = await addDoc(collection(db, collectionPath), cloneItem);
424
- if (Object.prototype.hasOwnProperty.call(data, collectionPath)) {
425
- data[collectionPath][docRef.id] = cloneItem;
426
- }
427
- storeDoc(collectionPath, { ...cloneItem, docId: docRef.id });
428
- }
429
- };
430
-
431
- // Composable to delete a document
432
- export const removeDoc = (collectionPath: string, docId: string): void => {
433
- // Just in case getting collection back from firebase is slow:
434
- if (Object.prototype.hasOwnProperty.call(data, collectionPath)) {
435
- if (Object.prototype.hasOwnProperty.call(data[collectionPath], docId)) {
436
- delete data[collectionPath][docId];
437
- }
438
- }
439
- deleteDoc(doc(db, collectionPath, docId));
440
- };
441
-
442
- // Composable to stop snapshot listener
443
- export const stopSnapshot = (collectionPath: string): void => {
444
- if (unsubscibe[collectionPath] instanceof Function) {
445
- unsubscibe[collectionPath]();
446
- unsubscibe[collectionPath] = null;
1
+ import { EdgeFirebase } from "./edgeFirebase";
2
+ export default {
3
+ install: (app, options) => {
4
+ const eFb = new EdgeFirebase(options);
5
+ app.provide("edgeFirebase", eFb);
447
6
  }
448
7
  };
8
+ export { EdgeFirebase };
package/package.json CHANGED
@@ -1,9 +1,14 @@
1
1
  {
2
2
  "name": "@edgedev/firebase",
3
- "version": "1.1.6",
4
- "description": "Composables and stores for firebase",
3
+ "version": "1.2.2",
4
+ "description": "Vue 3 / Nuxt 3 Plugin or Nuxt 3 global composable for firebase authenication and firestore",
5
5
  "main": "index.ts",
6
6
  "author": "Seth Fischer",
7
+ "keywords": [
8
+ "firebase",
9
+ "nuxt 3",
10
+ "vue 3"
11
+ ],
7
12
  "license": "ISC",
8
13
  "publishConfig": {
9
14
  "access": "public"
@@ -14,17 +19,14 @@
14
19
  "@typescript-eslint/parser": "^5.40.0",
15
20
  "eslint": "^8.25.0",
16
21
  "eslint-config-prettier": "^8.5.0",
22
+ "firebase": "^9.12.1",
17
23
  "prettier": "^2.7.1",
18
24
  "typescript": "^4.8.4",
19
- "vite": "^3.1.8",
20
- "vue": "^3.2.41",
21
- "firebase": "^9.12.1"
25
+ "vue": "^3.2.41"
22
26
  },
23
- "dependencies": {},
24
27
  "peerDependencies": {
25
28
  "firebase": "^9.12.1",
26
- "vue": "^3.0.0",
27
- "vite": "^3.1.8"
29
+ "vue": "^3.0.0"
28
30
  },
29
31
  "scripts": {
30
32
  "test": "echo \"Error: no test specified\" && exit 1"