Skip to content
This repository was archived by the owner on Apr 4, 2023. It is now read-only.

Commit e8406dc

Browse files
committed
[query] - Rework firebaseWebApi queries to allow chaining of filters. iOS implementation
1 parent 9c193fc commit e8406dc

File tree

2 files changed

+190
-13
lines changed

2 files changed

+190
-13
lines changed

src/firebase.android.ts

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
GetAuthTokenOptions,
1212
GetAuthTokenResult,
1313
OnDisconnect as OnDisconnectBase, QueryOptions,
14+
Query as QueryBase,
1415
User
1516
} from "./firebase";
1617
import {
@@ -21,15 +22,7 @@ import {
2122
isDocumentReference
2223
} from "./firebase-common";
2324
import * as firebaseFunctions from "./functions/functions";
24-
<<<<<<< HEAD
2525
import * as firebaseMessaging from "./messaging/messaging";
26-
=======
27-
import * as appModule from "tns-core-modules/application";
28-
import { AndroidActivityResultEventData } from "tns-core-modules/application";
29-
import { ad as AndroidUtils } from "tns-core-modules/utils/utils";
30-
import lazy from "tns-core-modules/utils/lazy";
31-
import { firestore, User, OnDisconnect as OnDisconnectBase, DataSnapshot, Query as QueryBase } from "./firebase";
32-
>>>>>>> [query] - Rework firebaseWebApi queries to allow chaining of filters. Android implementation
3326

3427
declare const com: any;
3528
const gmsAds = (<any>com.google.android.gms).ads;
@@ -1629,11 +1622,10 @@ firebase.update = (path, val) => {
16291622
}
16301623
});
16311624
};
1625+
firebase.query = (updateCallback, path, options) => {
1626+
return new Promise((resolve, reject) => {
1627+
16321628

1633-
<<<<<<< HEAD
1634-
firebase.query = (updateCallback: (data: FBDataSingleEvent | FBErrorData) => void, path: string, options: QueryOptions): Promise<any> => {
1635-
return new Promise<any>((resolve, reject) => {
1636-
=======
16371629
firebase.webQuery = (path: string): QueryBase => {
16381630
if (!firebase.initialized) {
16391631
console.error("Please run firebase.init() before firebase.query()");
@@ -1841,7 +1833,6 @@ class Query implements QueryBase {
18411833

18421834
firebase.query = (updateCallback, path, options) => {
18431835
return new Promise((resolve, reject) => {
1844-
>>>>>>> [query] - Rework firebaseWebApi queries to allow chaining of filters. Android implementation
18451836
try {
18461837
if (firebase.instance === null) {
18471838
reject("Run init() first!");

src/firebase.ios.ts

Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
GetAuthTokenOptions,
88
GetAuthTokenResult,
99
OnDisconnect as OnDisconnectBase, QueryOptions,
10+
Query as QueryBase,
1011
User
1112
} from "./firebase";
1213
import {
@@ -1384,6 +1385,191 @@ firebase.update = (path, val) => {
13841385
}
13851386
});
13861387
};
1388+
firebase.webQuery = (path: string): QueryBase => {
1389+
if (!firebase.initialized) {
1390+
console.error("Please run firebase.init() before firebase.query()");
1391+
throw new Error("FirebaseApp is not initialized. Make sure you run firebase.init() first");
1392+
}
1393+
const dbRef: FIRDatabaseReference = FIRDatabase.database().reference().child(path);
1394+
return new Query(dbRef, path);
1395+
};
1396+
1397+
class Query implements QueryBase {
1398+
private query: FIRDatabaseQuery; // Keep track of internal query state allowing us to chain filter/range/limit
1399+
private static eventListenerMap: Map<string, Array<any>> = new Map(); // A map to keep track all all the listeners attached for the specified eventType
1400+
1401+
constructor(private dbRef: FIRDatabaseReference, private path: string) { }
1402+
1403+
on(eventType: string, callback: (a: any, b?: string) => any): Promise<any> {
1404+
const onValueEvent = result => {
1405+
if (result.error) {
1406+
callback(result); // CAREFUL before we were calling result.error!
1407+
} else {
1408+
callback({
1409+
key: result.key,
1410+
val: () => result.value,
1411+
exists: () => !!result.value
1412+
});
1413+
}
1414+
};
1415+
return new Promise((resolve, reject) => {
1416+
try {
1417+
if (eventType === "value" || eventType === "child_added" || eventType === "child_changed"
1418+
|| eventType === "child_removed" || eventType === "child_moved") {
1419+
// This.query may not exist if we call on without any sorts
1420+
const firDatabaseHandle = this.query ? this.attachEventObserver(this.query, eventType, onValueEvent) :
1421+
this.attachEventObserver(this.dbRef, eventType, onValueEvent);
1422+
if (!Query.eventListenerMap.has(eventType)) {
1423+
Query.eventListenerMap.set(eventType, []);
1424+
}
1425+
Query.eventListenerMap.get(eventType).push(firDatabaseHandle); // We need to keep track of the listeners to fully remove them when calling off
1426+
} else {
1427+
reject("Invalid eventType. Use one of the following: 'value', 'child_added', 'child_changed', 'child_removed', or 'child_moved'");
1428+
return;
1429+
}
1430+
resolve();
1431+
} catch (ex) {
1432+
console.log("Error in firebase.addValueEventListener: " + ex);
1433+
reject(ex);
1434+
}
1435+
});
1436+
}
1437+
1438+
once(eventType: string): Promise<any> {
1439+
return new Promise((resolve, reject) => {
1440+
firebase.getValue(this.path).then(result => {
1441+
resolve({
1442+
key: result.key,
1443+
val: () => result.value,
1444+
exists: () => !!result.value
1445+
});
1446+
});
1447+
});
1448+
}
1449+
1450+
off(eventType?: string): void {
1451+
// Remove all events if none specified
1452+
if (!eventType) {
1453+
Query.eventListenerMap.forEach((value: any[], key: string) => {
1454+
firebase.removeEventListeners(value, this.path);
1455+
});
1456+
} else { // Remove only the event specified by the user
1457+
if (Query.eventListenerMap.get(eventType)) {
1458+
firebase.removeEventListeners(Query.eventListenerMap.get(eventType), this.path);
1459+
}
1460+
}
1461+
}
1462+
1463+
orderByChild(value: string): Query {
1464+
if (this.query) {
1465+
throw new Error("You can't combine multiple orderBy calls!");
1466+
}
1467+
this.query = this.dbRef.queryOrderedByChild(value);
1468+
return this;
1469+
}
1470+
1471+
orderByKey(): Query {
1472+
if (this.query) {
1473+
throw new Error("You can't combine multiple orderBy calls!");
1474+
}
1475+
this.query = this.dbRef.queryOrderedByKey();
1476+
return this;
1477+
}
1478+
1479+
orderByPriority(): Query {
1480+
if (this.query) {
1481+
throw new Error("You can't combine multiple orderBy calls!");
1482+
}
1483+
this.query = this.dbRef.queryOrderedByPriority();
1484+
return this;
1485+
}
1486+
1487+
orderByValue(): Query {
1488+
if (this.query) {
1489+
throw new Error("You can't combine multiple orderBy calls!");
1490+
}
1491+
this.query = this.dbRef.queryOrderedByValue();
1492+
return this;
1493+
}
1494+
1495+
// Unlike the order-by methods, you can combine multiple limit or range functions.
1496+
// For example, you can combine the startAt() and endAt() methods to limit the results to a specified range of values.
1497+
1498+
equalTo(value: any, key?: string): Query {
1499+
if (key) {
1500+
this.query = this.query.queryEqualToValueChildKey(value, key);
1501+
} else {
1502+
this.query = this.query.queryEqualToValue(value);
1503+
}
1504+
return this;
1505+
}
1506+
1507+
startAt(value: any, key?: string): Query {
1508+
if (key) {
1509+
this.query = this.query.queryStartingAtValueChildKey(value, key);
1510+
} else {
1511+
this.query = this.query.queryStartingAtValue(value);
1512+
}
1513+
return this;
1514+
}
1515+
1516+
endAt(value: any, key?: string): Query {
1517+
if (key) {
1518+
this.query = this.query.queryEndingAtValueChildKey(value, key);
1519+
} else {
1520+
this.query = this.query.queryEndingAtValue(value);
1521+
}
1522+
return this;
1523+
}
1524+
1525+
limitToFirst(value: number): Query {
1526+
this.query = this.query.queryLimitedToFirst(value);
1527+
return this;
1528+
}
1529+
1530+
limitToLast(value: number): Query {
1531+
this.query = this.query.queryLimitedToLast(value);
1532+
return this;
1533+
}
1534+
1535+
/**
1536+
* Depending on the eventType, attach listeners at the specified Database location. Follow the WebApi which listens
1537+
* to specific events (Android is more generic value / child - which includes all events add, change, remove etc).
1538+
* Similar to firebase._addObserver but I do not want to listen for every event
1539+
*/
1540+
private attachEventObserver(dbRef: FIRDatabaseQuery | FIRDatabaseReference, eventType: string, callback): number {
1541+
let firEventType: FIRDataEventType;
1542+
switch (eventType) {
1543+
case "value":
1544+
firEventType = FIRDataEventType.Value;
1545+
break;
1546+
case "child_added":
1547+
firEventType = FIRDataEventType.ChildAdded;
1548+
break;
1549+
case "child_changed":
1550+
firEventType = FIRDataEventType.ChildChanged;
1551+
break;
1552+
case "child_removed":
1553+
firEventType = FIRDataEventType.ChildRemoved;
1554+
break;
1555+
case "child_moved":
1556+
firEventType = FIRDataEventType.ChildMoved;
1557+
break;
1558+
}
1559+
1560+
const listener = dbRef.observeEventTypeWithBlockWithCancelBlock(
1561+
firEventType,
1562+
snapshot => {
1563+
callback(firebase.getCallbackData(eventType, snapshot));
1564+
},
1565+
firebaseError => {
1566+
callback({
1567+
error: firebaseError.localizedDescription
1568+
});
1569+
});
1570+
return listener;
1571+
}
1572+
}
13871573

13881574
firebase.query = (updateCallback: (data: FBDataSingleEvent) => void, path: string, options: QueryOptions): Promise<any> => {
13891575
return new Promise<any>((resolve, reject) => {

0 commit comments

Comments
 (0)