@edgedev/firebase 1.1.0 → 1.1.1

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.
Files changed (3) hide show
  1. package/README.md +249 -0
  2. package/index.ts +54 -43
  3. package/package.json +1 -1
package/README.md ADDED
@@ -0,0 +1,249 @@
1
+ # @edgedev/firebase
2
+
3
+ This a collection of vue3 composables of firebase functions
4
+
5
+ ### Table of Contents
6
+ **[Installation](#installation)**
7
+ **[Firebase Authentication](#firebase-authentication)**
8
+ **[Firestore Basic Document Interactions](#firestore-Basic-document-interactions)**
9
+ **[Firestore Snapshot Listeners](#firestore-snapshot-listeners)**
10
+ **[Firestore Static Collection Data](#firestore-static-collection-data)**
11
+
12
+ # Installation
13
+
14
+ pnpm install @edgedev/firebase
15
+
16
+ ```bash
17
+ pnpm install @edgedev/firebase
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.
20
+
21
+ ```typescript
22
+ import * as edgeFirebase from "@edgedev/firebase";
23
+ export { edgeFirebase };
24
+ ```
25
+
26
+ Also or Nuxt 3 SSR must be disabled, update the nuxt.config.ts file:
27
+ ```javascript
28
+ export default defineNuxtConfig({ ssr: false });
29
+ ```
30
+
31
+ If not in Nuxt 3 or there is no need for it to be global, put this in your components <script setup>
32
+
33
+ ```javascript
34
+ <script setup>
35
+ import * as edgeFirebase from "@edgedev/firebase";
36
+ </script>
37
+ ```
38
+ <a name="Firebase Authentication>
39
+ # Firebase Authentication
40
+ (currently only sign in with email and password is supported)
41
+
42
+ If "persistence" is true, login will be saved locally, they can close their browser and when they open they will be logged in automatically. If "persistence" is false login saved only for the session.
43
+ ```javascript
44
+ edgeFirebase.logIn(
45
+ {
46
+ email: "devs@edgemarketing.com",
47
+ password: "pasword"
48
+ },
49
+ true // : persistence
50
+ );
51
+ ```
52
+
53
+ #### User information is contained in: edgeFirebase.user
54
+ The user object is reactive and contains these items:
55
+ ```typescript
56
+ interface UserDataObject {
57
+ uid: string | null;
58
+ email: string;
59
+ loggedIn: boolean;
60
+ logInError: boolean;
61
+ logInErrorMessage: string;
62
+ }
63
+ ```
64
+ The reactive item **edgeFirebase.user.loggedIn** can be used in code or templates to determine if they user is logged in.
65
+
66
+ If there is an error logging in, **edgeFirebase.user.logInError** will be true and **edgeFirebase.user.logInErrorMessage** can be used to return that error to the user.
67
+
68
+ After logging in, **edgeFirebase.logOut** becomes available. Logging out will also automatically disconnect all FireStore listeners.
69
+
70
+ Here is a sample component using the login:
71
+ ```html
72
+ <template>
73
+ <div>
74
+ <div v-if="edgeFirebase.user.loggedIn">
75
+ <button @click="edgeFirebase.logOut">Logout</button><br />
76
+ <AppUsers v-if="edgeFirebase.user.loggedIn" />
77
+ </div>
78
+ <div v-else>
79
+ <input v-model="email" style="width: 400px" type="text" /><br />
80
+ <input v-model="password" style="width: 400px" type="text" /><br />
81
+ <button @click="login">Login</button><br />
82
+ <div v-if="edgeFirebase.user.logInError">
83
+ {{ edgeFirebase.user.logInErrorMessage }}
84
+ </div>
85
+ </div>
86
+ </div>
87
+ </template>
88
+ ```
89
+
90
+ ```javascript
91
+ <script setup>
92
+ const email = ref("");
93
+ const password = ref("");
94
+ const login = () => {
95
+ edgeFirebase.logIn(
96
+ {
97
+ email: email.value,
98
+ password: password.value
99
+ },
100
+ true
101
+ );
102
+ };
103
+ </script>
104
+ ```
105
+ # Firestore Basic Document Interactions
106
+ ### Adding/Update a Document.
107
+ Both adding and updating a document use the same function: **edgeFirebase.storeDoc(collectionPath, object)** for a document to be updated the object must contain the key **docId** and the value must match the ID of a document in the collection on are updating *(Note: All documents returned by edgeFirebase functions will already have docId insert in the document objects)*. If the object does not contain docId or the docId doesn't match a document in the collection, new document will be created.
108
+
109
+ ```javascript
110
+ <script setup>
111
+ const addUser = {name: "bob"};
112
+ edgeFirebase.storeDoc("users", addUser);
113
+ </script>
114
+ ```
115
+ Note: When a document is written to the collection several other keys are added that can be referenced: **doc_created_at**(timestamp of doc creation), **last_updated**(timestamp document last written), **uid**(the user id of the user that updated or created the document).
116
+
117
+ ### Getting a single Document.
118
+ If you want to query a single document from a collection use: **edgeFirebase.getDocData(collectionPath, docId)**
119
+ ```javascript
120
+ <script setup>
121
+ const docId = "DrJRpDXVsEEqZu0UB8NT";
122
+ const singleDoc = edgeFirebase.getDocData("users", docId);
123
+ </script>
124
+ ```
125
+
126
+ ### Deleting a Document.
127
+ To delete a document use: **edgeFirebase.removeDoc(collectionPath, docId)**
128
+ ```javascript
129
+ <script setup>
130
+ const docId = "DrJRpDXVsEEqZu0UB8NT";
131
+ const singleDoc = edgeFirebase.removeDoc("users", docId);
132
+ </script>
133
+ ```
134
+
135
+ # Firestore Snapshot Listeners
136
+ ### Starting a snapshot listener on a collection.
137
+ To start a snapshot listen on a collection use: **edgeFirebase.startSnapshot(collectionPath)**
138
+ ```javascript
139
+ <script setup>
140
+ edgeFirebase.startSnapshot("users");
141
+ </script>
142
+ ```
143
+ Once you have started a snapshot reactive data for that snapshot will be available with **edgeFirebase.data[collectionPath]**. Each document in the data object is keyed with the DocumentId from FireStore.
144
+ ```html
145
+ <template>
146
+ <div>
147
+ <div v-for="item in edgeFirebase.data.users" :key="item">
148
+ {{ item.name }}
149
+ </div>
150
+ </div>
151
+ </template>
152
+ ```
153
+ ### Snapshot listeners can also be queried, sorted, and limited.
154
+ #### Query and Sort are an array of objects, Limit is a number
155
+ (if passing more than one query on different keys, FireStore may make you create indexes)
156
+ ```typescript
157
+ interface FirestoreQuery {
158
+ field: string;
159
+ operator: WhereFilterOp; // '==' | '<' | '<=' | '>' | '>=' | 'array-contains' | 'in' | 'array-contains-any';
160
+ value: unknown;
161
+ }
162
+
163
+ interface FirestoreOrderBy {
164
+ field: string;
165
+ direction: "asc" | "desc";
166
+ }
167
+ ```
168
+ ##### Example with query, sort and limit:
169
+ ```javascript
170
+ <script setup>
171
+ const query = [{field: "name", operator: "==", value="Bob"}];
172
+ const sort = [{ field: "name", direction: "asc" }];
173
+ const limit = 10;
174
+ edgeFirebase.startSnapshot("users", query, sort, limit);
175
+ </setup>
176
+ ```
177
+ ### Stopping a snapshot listener
178
+ To stop listening to a collection use: **edgeFirebase.stopSnapshot(collectionPath)**
179
+ ```javascript
180
+ <script setup>
181
+ edgeFirebase.stopSnapshot("users");
182
+ </setup>
183
+ ```
184
+
185
+ # Firestore Static Collection Data
186
+ To get static data from a collection use the Object: **edgeFirebase.SearchStaticData()**. Static search is done from a class to handle pagination better.
187
+ ```javascript
188
+ const staticSearch = new edgeFirebase.SearchStaticData();
189
+ staticSearch.getData("users");
190
+ ```
191
+ After initialized like above... Data will be available from **staticSearch.results.data**
192
+
193
+ ### The static data object can also be queried, sorted, limited and paginated.
194
+ (if passing more than one query on different keys, FireStore may make you create indexes)
195
+ ```typescript
196
+ interface FirestoreQuery {
197
+ field: string;
198
+ operator: WhereFilterOp; // '==' | '<' | '<=' | '>' | '>=' | 'array-contains' | 'in' | 'array-contains-any';
199
+ value: unknown;
200
+ }
201
+
202
+ interface FirestoreOrderBy {
203
+ field: string;
204
+ direction: "asc" | "desc";
205
+ }
206
+ ```
207
+
208
+ ### Pagination
209
+
210
+ For pagination purposes there are 2 functions **staticSearch.next()** and **staticSearch.prev()**
211
+ for updating **staticSearch.results.data** the pagination data set. There are also two helper variables **staticSearch.results.staticIsFirstPage** (set to true if the data is at the first pagination data set) and **staticSearch.results.staticIsLastPage** (set to true if the data is on the last pagination data set). Note: Because of the way Firestore pagination works, you don't know you are at your last data set until you try and query for the next. If you are using using **staticSearch.results.staticIsLastPage** to disable a "Next" button for example it won't happen until the "second" click and in that scenario **staticSearch.results.data** will just remain at the last pagination data set, it won't break.
212
+
213
+ ### Example - Template and code with query, sort, limit, and pagination:
214
+ ```html
215
+ <template>
216
+ <div>
217
+ <div v-for="item in staticSearch.results.data" :key="item">
218
+ {{ item.name }}
219
+ </div>
220
+ <div>
221
+ <button
222
+ v-if="!staticSearch.results.staticIsFirstPage"
223
+ @click="staticSearch.prev()"
224
+ >
225
+ Previous
226
+ </button>
227
+ <button
228
+ v-if="!staticSearch.results.staticIsLastPage"
229
+ @click="staticSearch.next()"
230
+ >
231
+ Next
232
+ </button>
233
+ </div>
234
+ </div>
235
+ </template>
236
+ ```
237
+ ```javascript
238
+ <script setup>
239
+ const staticSearch = new edgeFirebase.SearchStaticData();
240
+
241
+ const query = [{field: "name", operator: "==", value="Bob"}];
242
+ const sort = [{ field: "name", direction: "asc" }];
243
+ const limit = 10;
244
+
245
+ staticSearch.getData("users", query, sort, limit);
246
+ </script>
247
+ ```
248
+ ## License
249
+ [ISC](https://choosealicense.com/licenses/isc/)
package/index.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { initializeApp } from "firebase/app";
2
- import { reactive, ref } from "vue";
2
+ import { reactive } from "vue";
3
3
 
4
4
  import {
5
5
  getFirestore,
@@ -207,43 +207,47 @@ export const getStaticData = async (
207
207
  return { data, next: nextLast };
208
208
  };
209
209
 
210
+ // Class for wrapping a getSaticData to handle pagination
210
211
  export class SearchStaticData {
211
212
  collectionPath = "";
212
213
  queryList: FirestoreQuery[] = [];
213
214
  orderList: FirestoreOrderBy[] = [];
214
215
  max = 0;
215
216
 
216
- data = ref({});
217
- pagination = ref([]);
218
- staticIsLastPage = ref<boolean>(true);
219
- staticIsFirstPage = ref<boolean>(true);
220
- staticCurrentPage = ref("");
217
+ results = reactive({
218
+ data: {},
219
+ pagination: [],
220
+ staticIsLastPage: true,
221
+ staticIsFirstPage: true,
222
+ staticCurrentPage: ""
223
+ });
221
224
 
222
225
  prev = async (): Promise<void> => {
223
- const findIndex = this.pagination.value.findIndex(
224
- (x) => x.key === this.staticCurrentPage.value
226
+ const findIndex = this.results.pagination.findIndex(
227
+ (x) => x.key === this.results.staticCurrentPage
225
228
  );
226
229
  let last = null;
227
230
  if (findIndex === 1) {
228
- this.staticCurrentPage.value = "";
229
- this.staticIsLastPage.value = false;
230
- this.staticIsFirstPage.value = true;
231
+ this.results.staticCurrentPage = "";
232
+ this.results.staticIsLastPage = false;
233
+ this.results.staticIsFirstPage = true;
231
234
  } else {
232
- last = this.pagination.value[findIndex - 2].next;
233
- this.staticCurrentPage.value = this.pagination.value[findIndex - 2].key;
235
+ last = this.results.pagination[findIndex - 2].next;
236
+ this.results.staticCurrentPage =
237
+ this.results.pagination[findIndex - 2].key;
234
238
  }
235
239
  await this.afterNextPrev(last);
236
240
  };
237
241
 
238
242
  next = async (): Promise<void> => {
239
- const findIndex = this.pagination.value.findIndex(
240
- (x) => x.key === this.staticCurrentPage.value
243
+ const findIndex = this.results.pagination.findIndex(
244
+ (x) => x.key === this.results.staticCurrentPage
241
245
  );
242
- const last = this.pagination.value[findIndex].next;
243
- if (this.pagination.value.length === 1) {
244
- this.staticIsFirstPage.value = true;
246
+ const last = this.results.pagination[findIndex].next;
247
+ if (this.results.pagination.length === 1) {
248
+ this.results.staticIsFirstPage = true;
245
249
  } else {
246
- this.staticIsFirstPage.value = false;
250
+ this.results.staticIsFirstPage = false;
247
251
  }
248
252
  await this.afterNextPrev(last);
249
253
  };
@@ -258,15 +262,15 @@ export class SearchStaticData {
258
262
  );
259
263
 
260
264
  if (last && Object.keys(results.data).length === 0) {
261
- this.staticIsLastPage.value = true;
262
- if (this.pagination.value.length === 1) {
265
+ this.results.staticIsLastPage = true;
266
+ if (this.results.pagination.length === 1) {
263
267
  last = null;
264
- this.staticCurrentPage.value = "";
265
- this.staticIsFirstPage.value = true;
268
+ this.results.staticCurrentPage = "";
269
+ this.results.staticIsFirstPage = true;
266
270
  } else {
267
- last = this.pagination.value[this.pagination.value.length - 2].next;
268
- this.staticCurrentPage.value =
269
- this.pagination.value[this.pagination.value.length - 2].key;
271
+ last = this.results.pagination[this.results.pagination.length - 2].next;
272
+ this.results.staticCurrentPage =
273
+ this.results.pagination[this.results.pagination.length - 2].key;
270
274
  }
271
275
  results = await getStaticData(
272
276
  "users",
@@ -276,20 +280,20 @@ export class SearchStaticData {
276
280
  last
277
281
  );
278
282
  } else {
279
- this.staticIsLastPage.value = false;
280
- if (this.pagination.value.length === 1) {
281
- this.staticIsFirstPage.value = false;
283
+ this.results.staticIsLastPage = false;
284
+ if (this.results.pagination.length === 1) {
285
+ this.results.staticIsFirstPage = false;
282
286
  }
283
287
  }
284
- this.data.value = results.data;
285
- this.staticCurrentPage.value = results.next.id;
286
- if (!this.staticIsLastPage.value) {
288
+ this.results.data = results.data;
289
+ this.results.staticCurrentPage = results.next.id;
290
+ if (!this.results.staticIsLastPage) {
287
291
  if (results.next) {
288
- const findItem = this.pagination.value.find(
292
+ const findItem = this.results.pagination.find(
289
293
  (x) => x.key === results.next.id
290
294
  );
291
295
  if (!findItem) {
292
- this.pagination.value.push({
296
+ this.results.pagination.push({
293
297
  key: results.next.id,
294
298
  next: results.next
295
299
  });
@@ -308,12 +312,12 @@ export class SearchStaticData {
308
312
  this.queryList = queryList;
309
313
  this.orderList = orderList;
310
314
  this.max = max;
311
- this.staticIsLastPage.value = false;
312
- this.staticIsFirstPage.value = true;
313
- this.staticCurrentPage.value = "";
314
- this.pagination.value = [];
315
- this.pagination.value = [];
316
- this.data.value = {};
315
+ this.results.staticIsLastPage = true;
316
+ this.results.staticIsFirstPage = true;
317
+ this.results.staticCurrentPage = "";
318
+ this.results.pagination = [];
319
+ this.results.pagination = [];
320
+ this.results.data = {};
317
321
  const results = await getStaticData(
318
322
  collectionPath,
319
323
  queryList,
@@ -321,9 +325,16 @@ export class SearchStaticData {
321
325
  max
322
326
  );
323
327
  if (Object.keys(results.data).length > 0) {
324
- this.data.value = results.data;
325
- this.staticCurrentPage.value = results.next.id;
326
- this.pagination.value.push({ key: results.next.id, next: results.next });
328
+ this.results.staticIsLastPage = false;
329
+ this.results.data = results.data;
330
+ this.results.staticCurrentPage = results.next.id;
331
+ this.results.pagination.push({
332
+ key: results.next.id,
333
+ next: results.next
334
+ });
335
+ } else {
336
+ this.results.staticIsLastPage = true;
337
+ this.results.staticIsFirstPage = true;
327
338
  }
328
339
  };
329
340
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@edgedev/firebase",
3
- "version": "1.1.0",
3
+ "version": "1.1.1",
4
4
  "description": "Composables and stores for firebase",
5
5
  "main": "index.ts",
6
6
  "author": "Seth Fischer",