-
Notifications
You must be signed in to change notification settings - Fork 0
/
pomegranate.coffee
309 lines (231 loc) · 8.48 KB
/
pomegranate.coffee
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
#
# Utilities
#
U =
log : ( message, details ) ->
U.output message, details, "log-info"
error : ( message, details ) ->
U.output message, details, "log-error"
output : ( message, details, cssClass ) ->
time = moment().format("D-MMM h:mm:ss")
random = Math.random().toString(36).substr(2)
console.log message
details = "<div class='log-details'>#{details}</div>" if details?
$("#log").append "<div class='#{cssClass}'>#{time} #{message}<span id='log-status-#{random}'></span>#{details||""}<div>"
exec : ( options ) ->
cordova.exec options.success, options.error, options.plugin.name, options.plugin.function, options.plugin.args
status : ( id, status, name) ->
$status = $("#status-#{id}")
unless $status.length is 0
$("#status-#{id}").html(status)
return
name = id unless name?
$("#status").html("<div><b>#{name}</b>: <span id='status-#{status}'>#{status}</span></div>")
#
# Database operations
#
_db = # gets added to pouchDb object
replicateBothWays : () ->
if P.config.local_database_name and P.config.remote_url
U.log "Replicating from #{P.config.local_database_name} to #{P.config.remote_url} and vice versa"
replications =
to: PouchDB.replicate P.config.local_database_name, P.config.remote_url,
continuous: true
complete: (response) ->
U.log JSON.stringify response
from: PouchDB.replicate P.config.remote_url, P.config.local_database_name,
continuous: true
complete: (response) ->
U.log JSON.stringify response
U.log JSON.stringify replications
else
U.log "Waiting for configuration to load or be entered, waiting 5 seconds"
_.delay replicateBothWays, 5000
saveResults : (results) ->
if results.msgs.length isnt 0
_(results.msgs).each (msg) ->
newDoc = msg
# Make this deterministic to allow for syncing after deleting the database locally (and syncing with cloud)
newDoc._id = "#{msg.time_received}+#{msg.from}"
P.db.put msg, (error, response) ->
U.error("Error while saving", JSON.stringify error) if error and error.status isnt 409
if response? and not response.error
U.log "New message saved", msg.message
saveAllSms : ->
U.exec
success: _db.saveResults
error: (error) ->
U.error "Error while saving", JSON.stringify error
plugin:
name: "ReadSms"
function: "GetTexts"
args: ["",-1]
saveAllSmsAfter : (cutoff) ->
U.exec
success: _db.saveResults
error: (error) ->
U.error "Error while saving", JSON.stringify error
plugin:
name: "ReadSms"
function: "GetTextsAfter"
args: ["", cutoff, -1]
P = {}
#
# Views
#
P.views =
msgsSent : (doc) ->
emit(doc._id, null) if doc.to and doc.processed
msgsRecieved : (doc) ->
emit(doc._id, null) if doc.time_received and doc.processed
msgsUnprocessed: (doc) ->
isUnprocessed = not doc.processed
isMessage = doc.message
emit(doc._id, null) if isMessage and isUnprocessed
msgsByTimeReceived : (doc) ->
emit(doc.time_received, null) if doc.time_received
msgsToSend: (doc) ->
needsToGo = doc.to and not doc.processed
isMessage = doc.message
emit(doc._id, null) if isMessage and needsToGo
#
# Filters
#
P.filters =
messageNeedsToGo: (doc) ->
needsToGo = doc.to and not doc.processed
isMessage = doc.message
return isMessage and needsToGo
msgsUnprocessed: (doc) ->
wasReceived = doc.time_received
isUnprocessed = not doc.processed
return wasReceived and isUnprocessed
#
# Config
#
P.config =
local_database_name : "pomegranate"
remote_couch : "http://pomegranate.tangerinecentral.org"
sync_previous_days : 10
#
# Boot, called at deviceready
#
P.boot = ->
# Run in background
console.log "Starting service"
cordova.define 'cordova/plugin/myService', (require,exports,module) ->
CreateBackgroundService('org.rti.pomegranate.MyService', require, exports, module)
(cordova.require('cordova/plugin/myService')).startService( (r) -> console.log "Started #{JSON.stringify r}", (r) -> console.log "Failed to start #{JSON.stringify r}")
console.log "Service started"
P.sender = cordova.require('cordova/plugin/smssendingplugin')
P.sender.isSupported (supported) ->
alert "Error\n\nThis device does not support SMS." unless supported
, ->
console.log "Error while checking for SMS support"
U.log "Starting DB"
try
P.db = new PouchDB(P.config.local_database_name, adapter: "websql")
U.log "Using WebSQL adapter"
catch e
U.error "WebSQL database failed", e
try
P.db = new PouchDB(P.config.local_database_name, adapter: "idb")
U.log "Using IDB adapter"
catch e
U.error "IDB database failed", e
try
P.db = new PouchDB(P.config.local_database_name, adapter: "leveldb")
catch e
U.error "LevelDB database failed", e
$.extend(P.db, _db)
console.log "Trying to load config from database"
P.db.get 'config',
(error,doc) ->
if doc?
console.log "Found a config: #{JSON.stringify doc}"
P.config = doc
else
bootbox.prompt "Enter the project name at #{P.config.remote_couch}", (projectName) ->
P.config = _.extend P.config,
_id : "config"
remote_url : "#{P.config.remote_couch}/#{projectName}"
P.db.put P.config, ->
location.reload()
$ ->
resize = -> $("#log").height( ($(window).height()-($("#log").position().top+($(window).height()*0.2))) + "px" )
touchScroll('log')
$(window).on "resize", resize
resize()
U.log "Starting application"
P.startApp()
#
# Start the application
#
P.startApp = ->
#
# DB events
#
# Check the db for the most recent messages
# Then add any new ones on the phone to it
checkMsgs = (callback) ->
# Update status
P.db.query { map: P.views.msgsSent }, { reduce: false }
, ( error, response ) -> U.status("sent", response.rows.length)
P.db.query { map: P.views.msgsRecieved }, { reduce: false }
, ( error, response ) -> U.status("received", response.rows.length)
P.db.query { map: P.views.msgsUnprocessed }, { reduce: false }
, ( error, response ) -> U.status("processing", response.rows.length)
P.db.query { map: P.views.msgsByTimeReceived }, { reduce: false }
, ( error, response ) -> U.status("db", response.rows.length)
# check for new messages
P.db.query { map: P.views.msgsByTimeReceived }
, {
reduce : false
include_docs : true
limit : 1
descending : true
}
, ( error, response ) ->
U.error("Error querying database", JSON.stringify error) if error?
if response.rows.length is 0 or not response.rows[0]?.doc?.time_received?
cutoffTime = moment().subtract('days', P.config.sync_previous_days)
P.db.saveAllSmsAfter cutoffTime.valueOf()
else
time = moment(parseInt(response.rows[0].doc.time_received)).format("d-MMM hh:mm")
P.db.saveAllSmsAfter(response.rows[0].doc.time_received)
# all the other checks
window.checkAgainInterval = setInterval checkMsgs, 10 * 1000
checkMsgs()
# Handle messages that need sending
P.db.changes
continuous : true
include_docs : true
filter : P.filters.messageNeedsToGo
onChange: (change) ->
doc = change.doc
doc.time_sent = (new Date()).getTime()
U.log "Message to send", JSON.stringify _(doc).without
P.sender.send
to : doc.to
message : doc.message
success: ->
U.log "Message sent", JSON.stringify doc
doc.processed = true
P.db.put doc
error: (error) ->
U.error "Error sending message: #{JSON.stringify error}", JSON.stringify doc
# Handle unprocessed received messages
P.db.changes
continuous : true
include_docs : true
filter: P.filters.msgsUnprocessed
onChange: (change) ->
# TODO this is where we can run some user defined triggers to post to google spreadsheet, send acknowledgements, etc
U.log "Message processed", JSON.stringify change.doc
change.doc.processed = true
P.db.put change.doc
P.db.replicateBothWays() # ...
document.addEventListener "online", ( -> U.log('Phone online'); P.db.replicateBothWays()), false
document.addEventListener "offline",( -> U.log('Phone offline')), false
# one for the money
document.addEventListener "deviceready", P.boot, false