diff --git a/.gitignore b/.gitignore index 6acbbf7..f83423e 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ .git.safe +/node_modules diff --git a/Gruntfile.js b/Gruntfile.js new file mode 100644 index 0000000..42595b0 --- /dev/null +++ b/Gruntfile.js @@ -0,0 +1,47 @@ +module.exports = function(grunt) { + + // Project configuration. + grunt.initConfig({ + jshint: { + options: { + curly: true, + eqeqeq: true, + immed: true, + latedef: true, + newcap: true, + noarg: true, + sub: true, + undef: true, + boss: true, + eqnull: true, + node: true, + es5: true, + globals: { + jasmine: false, + describe: false, + beforeEach: false, + afterEach: false, + expect: false, + it: false, + spyOn: false, + $: false, + cordova: false, + launchnavigator: false, + window: false, + document: false, + ons: false, + navigator: false, + google: false, + FCMPlugin: false, + device: false, + plugins: false, + addFixture: false, + truncateSql: false + } + }, + all: ['Gruntfile.js', 'www/**/*.js'] + } + }); + + grunt.loadNpmTasks('grunt-contrib-jshint'); +}; diff --git a/package.json b/package.json index 3ac1429..252f6de 100644 --- a/package.json +++ b/package.json @@ -8,5 +8,9 @@ "ios" ] }, - "description": "A Google Firebase Firestore plugin" + "description": "A Google Firebase Firestore plugin", + "devDependencies": { + "grunt": "^1.0.1", + "grunt-contrib-jshint": "^1.1.0" + } } diff --git a/src/android/uk/co/reallysmall/cordova/plugin/firestore/InitialiseHandler.java b/src/android/uk/co/reallysmall/cordova/plugin/firestore/InitialiseHandler.java index 890dd42..a37168f 100644 --- a/src/android/uk/co/reallysmall/cordova/plugin/firestore/InitialiseHandler.java +++ b/src/android/uk/co/reallysmall/cordova/plugin/firestore/InitialiseHandler.java @@ -8,10 +8,13 @@ import org.apache.cordova.CallbackContext; import org.json.JSONArray; import org.json.JSONException; +import org.json.JSONObject; public class InitialiseHandler implements ActionHandler { + public static final String PERSIST = "persist"; + public static final String DATE_PREFIX = "datePrefix"; private FirestorePlugin firestorePlugin; public InitialiseHandler(FirestorePlugin firestorePlugin) { @@ -25,11 +28,23 @@ public boolean handle(final JSONArray args, CallbackContext callbackContext) { if (firestorePlugin.getDatabase() == null) { Log.d(FirestorePlugin.TAG, "Initialising Firestore..."); - final boolean persist = args.getBoolean(0); + final JSONObject options = args.getJSONObject(0); FirebaseFirestore.setLoggingEnabled(true); firestorePlugin.setDatabase(FirebaseFirestore.getInstance()); + boolean persist = false; + + if (options.has(PERSIST) && options.getBoolean(PERSIST)) { + persist = true; + } + + if (options.has(DATE_PREFIX)) { + JSONDateWrapper.setDatePrefix(options.getString(DATE_PREFIX)); + } + + Log.d(FirestorePlugin.TAG, "Setting Firestore persistance to " + persist); + FirebaseFirestoreSettings settings = new FirebaseFirestoreSettings.Builder() .setPersistenceEnabled(persist) .build(); diff --git a/src/android/uk/co/reallysmall/cordova/plugin/firestore/JSONDateWrapper.java b/src/android/uk/co/reallysmall/cordova/plugin/firestore/JSONDateWrapper.java index 72c64e3..8375afc 100644 --- a/src/android/uk/co/reallysmall/cordova/plugin/firestore/JSONDateWrapper.java +++ b/src/android/uk/co/reallysmall/cordova/plugin/firestore/JSONDateWrapper.java @@ -3,12 +3,19 @@ import java.util.Date; public class JSONDateWrapper extends Date { + + private static String datePrefix = "__DATE:"; + public JSONDateWrapper(Date date) { super(date.getTime()); } + public static void setDatePrefix(String datePrefix) { + JSONDateWrapper.datePrefix = datePrefix; + } + @Override public String toString() { - return "__DATE(" + this.getTime() + ")"; + return this.datePrefix + this.getTime(); } } diff --git a/src/android/uk/co/reallysmall/cordova/plugin/firestore/JSONHelper.java b/src/android/uk/co/reallysmall/cordova/plugin/firestore/JSONHelper.java index aced40b..50de746 100644 --- a/src/android/uk/co/reallysmall/cordova/plugin/firestore/JSONHelper.java +++ b/src/android/uk/co/reallysmall/cordova/plugin/firestore/JSONHelper.java @@ -32,48 +32,6 @@ static JSONObject toJSON(Map values) throws JSONException { return result; } -// static Map jsonToMap(JSONObject json) throws JSONException { -// Map retMap = new HashMap(); -// -// if (json != JSONObject.NULL) { -// retMap = toMap(json); -// } -// return retMap; -// } - -// static Map toMap(JSONObject object) throws JSONException { -// Map map = new HashMap(); -// -// Iterator keysItr = object.keys(); -// while (keysItr.hasNext()) { -// String key = keysItr.next(); -// Object value = object.get(key); -// -// if (value instanceof JSONArray) { -// value = toList((JSONArray) value); -// } else if (value instanceof JSONObject) { -// value = toMap((JSONObject) value); -// } -// map.put(key, value); -// } -// return map; -// } -// -// -// static List toList(JSONArray array) throws JSONException { -// List list = new ArrayList(); -// for (int i = 0; i < array.length(); i++) { -// Object value = array.get(i); -// if (value instanceof JSONArray) { -// value = toList((JSONArray) value); -// } else if (value instanceof JSONObject) { -// value = toMap((JSONObject) value); -// } -// list.add(value); -// } -// return list; -// } - static Object toSettable(Object value) { Object result = value; diff --git a/www/browser/firestore.js b/www/browser/firestore.js index 4a189ff..31ead93 100644 --- a/www/browser/firestore.js +++ b/www/browser/firestore.js @@ -1,3 +1,5 @@ +/* global firebase: false, Promise: false */ + var PLUGIN_NAME = 'Firestore'; var loadJS = function(url, implementationCode, location) { @@ -10,24 +12,26 @@ var loadJS = function(url, implementationCode, location) { location.appendChild(scriptTag); }; -function Firestore(persist, firebaseOptions) { +function Firestore(options, resolve) { var self = this; var initialise = function() { - firebase.initializeApp(firebaseOptions); + firebase.initializeApp(options.browser); - if (persist) { + if (options.persist) { firebase.firestore().enablePersistence().then(function() { self.database = firebase.firestore(); + resolve(self); }); } else { self.database = firebase.firestore(); + resolve(self); } - } - loadJS('https://www.gstatic.com/firebasejs/4.5.0/firebase.js', function() { - loadJS('https://www.gstatic.com/firebasejs/4.5.0/firebase-firestore.js', initialise, document.body); + }; + loadJS('https://www.gstatic.com/firebasejs/4.7.0/firebase.js', function() { + loadJS('https://www.gstatic.com/firebasejs/4.7.0/firebase-firestore.js', initialise, document.body); }, document.body); } @@ -37,7 +41,9 @@ Firestore.prototype.get = function() { }; module.exports = { - initialise: function(persist, firebaseOptions) { - return new Firestore(persist, firebaseOptions); + initialise: function(options) { + return new Promise(function(resolve, reject) { + var db = new Firestore(options, resolve); + }); } }; diff --git a/www/firestore.js b/www/firestore.js index 225f755..ba547be 100644 --- a/www/firestore.js +++ b/www/firestore.js @@ -1,19 +1,21 @@ +/* global Promise: false */ + var exec = require('cordova/exec'); var utils = require("cordova/utils"); var PLUGIN_NAME = 'Firestore'; +var FirestoreOptions = { + "datePrefix": "__DATE:", + "persist": true +}; -function Firestore(persist, datePrefix) { - if (datePrefix === undefined) { - this.datePrefix = "__DATE("; - } else { - this.datePrefix = datePrefix; +function Firestore(options) { + FirestoreOptions = options; + if (FirestoreOptions.datePrefix === undefined) { + this.datePrefix = "__DATE:"; } - if (persist === undefined) { - persist = true; - } - exec(function() {}, null, PLUGIN_NAME, 'initialise', [persist]); + exec(function() {}, null, PLUGIN_NAME, 'initialise', [FirestoreOptions]); } Firestore.prototype = { @@ -25,116 +27,106 @@ Firestore.prototype = { } }; -function Query(ref, queryType, value) { - this._ref = ref; - this._ref._queries.push({ - "queryType": queryType, - "value": value - }); -} - -Query.prototype = { - endAt: function(snapshotOrVarArgs) { - return new Query(this._ref, "endAt", snapshotOrVarArgs); - }, - endBefore: function(snapshotOrVarArgs) { - return new Query(this._ref, "endBefore", snapshotOrVarArgs); - }, - limit: function(limit) { - return new Query(this._ref, "limit", limit); - }, - orderBy: function(field, direction) { - if (direction === undefined) { - direction = "ASCENDING"; - } +function DocumentSnapshot(data) { + this._data = data; - var orderByField = { - "field": field, - "direction": direction - }; - return new Query(this._ref, "orderBy", orderByField); - }, - get: function() { - var args = [this._ref._path, this._ref._queries]; + if (data.exists) { + var keys = Object.keys(this._data._data); + for (var i = 0; i < keys.length; i++) { + var key = keys[i]; - return new Promise(function(resolve, reject) { - exec(resolve, reject, PLUGIN_NAME, 'collectionGet', args); - }).then(function(data) { - return new QuerySnapshot(data); - }); - }, - onSnapshot: function(callback, options) { + if (typeof this._data._data[key] === 'string' && this._data._data[key].startsWith(FirestoreOptions.datePrefix)) { + var length = this._data._data[key].length; + var prefixLength = FirestoreOptions.datePrefix.length; - callbackId = utils.createUUID(); - var args = [this._ref._path, this._ref._queries, options, callbackId]; + var timestamp = this._data._data[key].substr(prefixLength, length - prefixLength); - var callbackWrapper = function(data) { - callback(new QuerySnapshot(data)); + this._data._data[key] = new Date(parseInt(timestamp)); + } } - exec(callbackWrapper, function() {}, PLUGIN_NAME, 'collectionOnShapshot', args); + } +} - return function() { - exec(function() {}, function() {}, PLUGIN_NAME, 'collectionUnsubscribe', [callbackId]); - }; - }, - startAfter: function(snapshotOrVarArgs) { - return new Query(this._ref, "startAfter", snapshotOrVarArgs); +DocumentSnapshot.prototype = { + _fieldPath: function(obj, i) { + return obj[i]; }, - startAt: function(snapshotOrVarArgs) { - return new Query(this._ref, "startAt", snapshotOrVarArgs); + data: function() { + return this._data._data; }, - where: function(fieldPath, opStr, passedValue) { - var value; - if (passedValue instanceof Date) { - value = "__DATE(" + passedValue.getTime() + ")"; - } else { - value = passedValue; - } - var whereField = { - "fieldPath": fieldPath, - "opStr": opStr, - "value": value - }; - return new Query(this._ref, "where", whereField); + get: function(fieldPath) { + return fieldPath.split('.').reduce(this._fieldPath, this._data); } }; -function CollectionReference(path, id) { - this._path = path; - this._id = id; - this._ref = this; - this._queries = []; -} - -CollectionReference.prototype = Object.create(Query.prototype, { - firestore: { +Object.defineProperties(DocumentSnapshot.prototype, { + exists: { get: function() { - throw "CollectionReference.firestore: Not supported"; + return this._data.exists; } }, id: { get: function() { - return this._id; + return this._data.id; } }, - parent: { + metadata: { get: function() { - throw "CollectionReference.parent: Not supported"; + throw "DocumentReference.metadata: Not supported"; + } + }, + ref: { + get: function() { + return this._data.ref; } } }); -CollectionReference.prototype.add = function(data) { - var args = [this._path, data]; +function QuerySnapshot(data) { + this._data = data; +} - return new Promise(function(resolve, reject) { - exec(resolve, reject, PLUGIN_NAME, 'collectionAdd', args); - }); +QuerySnapshot.prototype = { + forEach: function(callback, thisArg) { + var keys = Object.keys(this._data.docs); + for (var i = 0; i < keys.length; i++) { + callback(new DocumentSnapshot(this._data.docs[i])); + } + } }; -CollectionReference.prototype.doc = function(id) { - return new DocumentReference(this, id); -} +Object.defineProperties(QuerySnapshot.prototype, { + docChanges: { + get: function() { + throw "QuerySnapshot.docChanges: Not supported"; + } + }, + docs: { + get: function() { + return this._data.docs; + } + }, + empty: { + get: function() { + return this._data.docs.length === 0; + } + }, + metadata: { + get: function() { + throw "QuerySnapshot.metadata: Not supported"; + } + }, + query: { + get: function() { + throw "QuerySnapshot.query: Not supported"; + } + }, + size: { + get: function() { + return this._data.docs.length; + } + } +}); function DocumentReference(collectionReference, id) { this._id = id; @@ -208,7 +200,6 @@ DocumentReference.prototype = { } }; - Object.defineProperties(DocumentReference.prototype, { firestore: { get: function() { @@ -227,109 +218,121 @@ Object.defineProperties(DocumentReference.prototype, { } }); -function DocumentSnapshot(data) { - this._data = data; +function Query(ref, queryType, value) { + this._ref = ref; + this._ref._queries.push({ + "queryType": queryType, + "value": value + }); +} - if (data.exists) { - var keys = Object.keys(this._data._data); - for (var i = 0; i < keys.length; i++) { - var key = keys[i]; +Query.prototype = { + endAt: function(snapshotOrVarArgs) { + return new Query(this._ref, "endAt", snapshotOrVarArgs); + }, + endBefore: function(snapshotOrVarArgs) { + return new Query(this._ref, "endBefore", snapshotOrVarArgs); + }, + limit: function(limit) { + return new Query(this._ref, "limit", limit); + }, + orderBy: function(field, direction) { + if (direction === undefined) { + direction = "ASCENDING"; + } - if (typeof this._data._data[key] === 'string' && this._data._data[key].startsWith("__DATE(")) { - var length = this._data._data[key].length; - var wrapperLength = "__DATE(".length; + var orderByField = { + "field": field, + "direction": direction + }; + return new Query(this._ref, "orderBy", orderByField); + }, + get: function() { + var args = [this._ref._path, this._ref._queries]; - var timestamp = this._data._data[key].substr(wrapperLength, length - wrapperLength - 1); + return new Promise(function(resolve, reject) { + exec(resolve, reject, PLUGIN_NAME, 'collectionGet', args); + }).then(function(data) { + return new QuerySnapshot(data); + }); + }, + onSnapshot: function(callback, options) { - this._data._data[key] = new Date(parseInt(timestamp)); - } - } - } -} + var callbackId = utils.createUUID(); + var args = [this._ref._path, this._ref._queries, options, callbackId]; -DocumentSnapshot.prototype = { - _fieldPath: function(obj, i) { - return obj[i]; + var callbackWrapper = function(data) { + callback(new QuerySnapshot(data)); + }; + exec(callbackWrapper, function() {}, PLUGIN_NAME, 'collectionOnShapshot', args); + + return function() { + exec(function() {}, function() {}, PLUGIN_NAME, 'collectionUnsubscribe', [callbackId]); + }; }, - data: function() { - return this._data._data; + startAfter: function(snapshotOrVarArgs) { + return new Query(this._ref, "startAfter", snapshotOrVarArgs); }, - get: function(fieldPath) { - return fieldPath.split('.').reduce(this._fieldPath, this._data); + startAt: function(snapshotOrVarArgs) { + return new Query(this._ref, "startAt", snapshotOrVarArgs); + }, + where: function(fieldPath, opStr, passedValue) { + var value; + if (passedValue instanceof Date) { + value = Firestore.datePrefix + passedValue.getTime(); + } else { + value = passedValue; + } + var whereField = { + "fieldPath": fieldPath, + "opStr": opStr, + "value": value + }; + return new Query(this._ref, "where", whereField); } }; -Object.defineProperties(DocumentSnapshot.prototype, { - exists: { +function CollectionReference(path, id) { + this._path = path; + this._id = id; + this._ref = this; + this._queries = []; +} + +CollectionReference.prototype = Object.create(Query.prototype, { + firestore: { get: function() { - return this._data.exists; + throw "CollectionReference.firestore: Not supported"; } }, id: { get: function() { - return this._data.id; - } - }, - metadata: { - get: function() { - throw "DocumentReference.metadata: Not supported"; + return this._id; } }, - ref: { + parent: { get: function() { - return this._data.ref; + throw "CollectionReference.parent: Not supported"; } } }); +CollectionReference.prototype.add = function(data) { + var args = [this._path, data]; -function QuerySnapshot(data) { - this._data = data; -} + return new Promise(function(resolve, reject) { + exec(resolve, reject, PLUGIN_NAME, 'collectionAdd', args); + }); +}; -QuerySnapshot.prototype = { - forEach: function(callback, thisArg) { - var keys = Object.keys(this._data.docs); - for (var i = 0; i < keys.length; i++) { - callback(new DocumentSnapshot(this._data.docs[i])); - } - } +CollectionReference.prototype.doc = function(id) { + return new DocumentReference(this, id); }; -Object.defineProperties(QuerySnapshot.prototype, { - docChanges: { - get: function() { - throw "QuerySnapshot.docChanges: Not supported"; - } - }, - docs: { - get: function() { - return this._data.docs; - } - }, - empty: { - get: function() { - return this._data.docs.length === 0; - } - }, - metadata: { - get: function() { - throw "QuerySnapshot.metadata: Not supported"; - } - }, - query: { - get: function() { - throw "QuerySnapshot.query: Not supported"; - } - }, - size: { - get: function() { - return this._data.docs.length; - } - } -}); module.exports = { - initialise: function() { - return new Firestore(); + initialise: function(options) { + return new Promise(function(resolve, reject) { + resolve(new Firestore(options)); + }); } };