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

Commit 9c193fc

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

File tree

1 file changed

+224
-6
lines changed

1 file changed

+224
-6
lines changed

src/firebase.android.ts

Lines changed: 224 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,15 @@ import {
2121
isDocumentReference
2222
} from "./firebase-common";
2323
import * as firebaseFunctions from "./functions/functions";
24+
<<<<<<< HEAD
2425
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
2533

2634
declare const com: any;
2735
const gmsAds = (<any>com.google.android.gms).ads;
@@ -1421,22 +1429,22 @@ firebase.keepInSync = (path, switchOn) => {
14211429
};
14221430

14231431
firebase._addObservers = (to, updateCallback) => {
1424-
const listener = new com.google.firebase.database.ChildEventListener({
1425-
onCancelled: databaseError => {
1432+
const listener: com.google.firebase.database.ChildEventListener = new com.google.firebase.database.ChildEventListener({
1433+
onCancelled: (databaseError: com.google.firebase.database.DatabaseError) => {
14261434
updateCallback({
14271435
error: databaseError.getMessage()
14281436
});
14291437
},
1430-
onChildAdded: (snapshot, previousChildKey) => {
1438+
onChildAdded: (snapshot: com.google.firebase.database.DataSnapshot, previousChildKey: string) => {
14311439
updateCallback(firebase.getCallbackData('ChildAdded', snapshot));
14321440
},
1433-
onChildRemoved: snapshot => {
1441+
onChildRemoved: (snapshot: com.google.firebase.database.DataSnapshot) => {
14341442
updateCallback(firebase.getCallbackData('ChildRemoved', snapshot));
14351443
},
1436-
onChildChanged: (snapshot, previousChildKey) => {
1444+
onChildChanged: (snapshot: com.google.firebase.database.DataSnapshot, previousChildKey: string) => {
14371445
updateCallback(firebase.getCallbackData('ChildChanged', snapshot));
14381446
},
1439-
onChildMoved: (snapshot, previousChildKey) => {
1447+
onChildMoved: (snapshot: com.google.firebase.database.DataSnapshot, previousChildKey: string) => {
14401448
updateCallback(firebase.getCallbackData('ChildMoved', snapshot));
14411449
}
14421450
});
@@ -1622,8 +1630,218 @@ firebase.update = (path, val) => {
16221630
});
16231631
};
16241632

1633+
<<<<<<< HEAD
16251634
firebase.query = (updateCallback: (data: FBDataSingleEvent | FBErrorData) => void, path: string, options: QueryOptions): Promise<any> => {
16261635
return new Promise<any>((resolve, reject) => {
1636+
=======
1637+
firebase.webQuery = (path: string): QueryBase => {
1638+
if (!firebase.initialized) {
1639+
console.error("Please run firebase.init() before firebase.query()");
1640+
throw new Error("FirebaseApp is not initialized. Make sure you run firebase.init() first");
1641+
}
1642+
const dbRef: com.google.firebase.database.DatabaseReference = firebase.instance.child(path);
1643+
return new Query(dbRef, path);
1644+
};
1645+
1646+
class Query implements QueryBase {
1647+
private query: com.google.firebase.database.Query; // Keep track of internal query state allowing us to chain filter/range/limit
1648+
private static eventListenerMap: Map<string, Array<any>> = new Map(); // A map to keep track all all the listeners attached for the specified eventType
1649+
1650+
constructor(private dbRef: com.google.firebase.database.DatabaseReference, private path: string) { }
1651+
1652+
on(eventType: string, callback: (a: any, b?: string) => any): Promise<any> {
1653+
const onValueEvent = result => {
1654+
if (result.error) {
1655+
callback(result); // CAREFUL before we were calling result.error!
1656+
} else {
1657+
callback({
1658+
key: result.key,
1659+
val: () => result.value,
1660+
exists: () => !!result.value
1661+
});
1662+
}
1663+
};
1664+
return new Promise((resolve, reject) => {
1665+
try {
1666+
if (firebase.instance === null) {
1667+
reject("Run init() first!");
1668+
return;
1669+
}
1670+
const listener = this.createEventListener(eventType, onValueEvent);
1671+
if (!this.query) this.query = this.dbRef; // Need this when calling on() without doing a sort as this.query is undefined
1672+
1673+
if (eventType === "value") {
1674+
this.query.addValueEventListener(listener as com.google.firebase.database.ValueEventListener);
1675+
} else if (eventType === "child_added" || eventType === "child_changed" || eventType === "child_removed" || eventType === "child_moved") {
1676+
this.query.addChildEventListener(listener as com.google.firebase.database.ChildEventListener);
1677+
} else {
1678+
reject("Invalid eventType. Use one of the following: 'value', 'child_added', 'child_changed', 'child_removed', or 'child_moved'");
1679+
return;
1680+
}
1681+
// Add listener to our map which keeps track of eventType: child/value events
1682+
if (!Query.eventListenerMap.has(eventType)) {
1683+
Query.eventListenerMap.set(eventType, []);
1684+
}
1685+
Query.eventListenerMap.get(eventType).push(listener); // We need to keep track of the listeners to fully remove them when calling off
1686+
resolve();
1687+
} catch (ex) {
1688+
console.log("Error in firebase.addValueEventListener: " + ex);
1689+
reject(ex);
1690+
}
1691+
});
1692+
}
1693+
1694+
once(eventType: string): Promise<any> {
1695+
return new Promise((resolve, reject) => {
1696+
firebase.getValue(this.path).then(result => {
1697+
resolve({
1698+
key: result.key,
1699+
val: () => result.value,
1700+
exists: () => !!result.value
1701+
});
1702+
});
1703+
});
1704+
}
1705+
1706+
off(eventType?: string): void {
1707+
// Remove all events if none specified
1708+
if (!eventType) {
1709+
Query.eventListenerMap.forEach((value: any[], key: string) => {
1710+
firebase.removeEventListeners(value, this.path);
1711+
});
1712+
} else { // Remove only the event specified by the user
1713+
if (Query.eventListenerMap.get(eventType)) {
1714+
firebase.removeEventListeners(Query.eventListenerMap.get(eventType), this.path);
1715+
}
1716+
}
1717+
}
1718+
1719+
orderByChild(value: string): Query {
1720+
if (this.query) {
1721+
throw new Error("You can't combine multiple orderBy calls!");
1722+
}
1723+
this.query = this.dbRef.orderByChild(value);
1724+
return this;
1725+
}
1726+
1727+
orderByKey(): Query {
1728+
if (this.query) {
1729+
throw new Error("You can't combine multiple orderBy calls!");
1730+
}
1731+
this.query = this.dbRef.orderByKey();
1732+
return this;
1733+
}
1734+
1735+
orderByPriority(): Query {
1736+
if (this.query) {
1737+
throw new Error("You can't combine multiple orderBy calls!");
1738+
}
1739+
this.query = this.dbRef.orderByPriority();
1740+
return this;
1741+
}
1742+
1743+
orderByValue(): Query {
1744+
if (this.query) {
1745+
throw new Error("You can't combine multiple orderBy calls!");
1746+
}
1747+
this.query = this.dbRef.orderByValue();
1748+
return this;
1749+
}
1750+
1751+
// Unlike the order-by methods, you can combine multiple limit or range functions.
1752+
// For example, you can combine the startAt() and endAt() methods to limit the results to a specified range of values.
1753+
1754+
equalTo(value: any, key?: string): Query {
1755+
if (key) {
1756+
this.query = this.query.equalTo(value, key);
1757+
} else {
1758+
this.query = this.query.equalTo(value);
1759+
}
1760+
return this;
1761+
}
1762+
1763+
startAt(value: any, key?: string): Query {
1764+
if (key) {
1765+
this.query = this.query.startAt(value, key);
1766+
} else {
1767+
this.query = this.query.startAt(value);
1768+
}
1769+
return this;
1770+
}
1771+
1772+
endAt(value: any, key?: string): Query {
1773+
if (key) {
1774+
this.query = this.query.endAt(value, key);
1775+
} else {
1776+
this.query = this.query.endAt(value);
1777+
}
1778+
return this;
1779+
}
1780+
1781+
limitToFirst(value: number): Query {
1782+
this.query = this.query.limitToFirst(value);
1783+
return this;
1784+
}
1785+
1786+
limitToLast(value: number): Query {
1787+
this.query = this.query.limitToLast(value);
1788+
return this;
1789+
}
1790+
/**
1791+
* Depending on the eventType, attach listeners at the specified Database location. Follow the WebApi which listens
1792+
* to specific events (Android is more generic value / child - which includes all events add, change, remove etc).
1793+
* Similar to firebase._addObserver but I do not want to listen for every event
1794+
*/
1795+
private createEventListener(eventType: string, callback): com.google.firebase.database.ValueEventListener | com.google.firebase.database.ChildEventListener {
1796+
let listener;
1797+
1798+
if (eventType === "value") {
1799+
listener = new com.google.firebase.database.ValueEventListener({
1800+
onDataChange: (snapshot: com.google.firebase.database.DataSnapshot) => {
1801+
callback(firebase.getCallbackData('ValueChanged', snapshot));
1802+
},
1803+
onCancelled: (databaseError: com.google.firebase.database.DatabaseError) => {
1804+
callback({
1805+
error: databaseError.getMessage()
1806+
});
1807+
}
1808+
});
1809+
} else if (eventType === "child_added" || eventType === "child_changed" || eventType === "child_removed" || eventType === "child_moved") {
1810+
listener = new com.google.firebase.database.ChildEventListener({
1811+
onCancelled: (databaseError: com.google.firebase.database.DatabaseError) => {
1812+
callback({
1813+
error: databaseError.getMessage()
1814+
});
1815+
},
1816+
onChildAdded: (snapshot: com.google.firebase.database.DataSnapshot, previousChildKey: string) => {
1817+
if (eventType === "child_added") {
1818+
callback(firebase.getCallbackData(eventType, snapshot));
1819+
}
1820+
},
1821+
onChildRemoved: (snapshot: com.google.firebase.database.DataSnapshot) => {
1822+
if (eventType === "child_removed") {
1823+
callback(firebase.getCallbackData(eventType, snapshot));
1824+
}
1825+
},
1826+
onChildChanged: (snapshot: com.google.firebase.database.DataSnapshot, previousChildKey: string) => {
1827+
if (eventType === "child_changed") {
1828+
callback(firebase.getCallbackData(eventType, snapshot));
1829+
}
1830+
},
1831+
onChildMoved: (snapshot: com.google.firebase.database.DataSnapshot, previousChildKey: string) => {
1832+
if (eventType === "child_moved") {
1833+
callback(firebase.getCallbackData(eventType, snapshot));
1834+
}
1835+
}
1836+
});
1837+
}
1838+
return listener;
1839+
}
1840+
}
1841+
1842+
firebase.query = (updateCallback, path, options) => {
1843+
return new Promise((resolve, reject) => {
1844+
>>>>>>> [query] - Rework firebaseWebApi queries to allow chaining of filters. Android implementation
16271845
try {
16281846
if (firebase.instance === null) {
16291847
reject("Run init() first!");

0 commit comments

Comments
 (0)