www/board/agenda/views/models/pending.js.rb (217 lines of code) (raw):

# # Provide a thin interface to the Server.pending data structure, and # implement the client side of offline processing. # class Pending Vue.util.defineReactive Server.pending, nil Vue.util.defineReactive Server.offline, false # fetch pending from server (needed for ServiceWorkers) def self.fetch() caches.open('board/agenda').then do |cache| fetched = false request = Request.new('pending.json', method: 'get', credentials: 'include', headers: {'Accept' => 'application/json'}) # use data from last cache until a response is received cache.match(request).then do |response| if response and not fetched response.json().then do |json| Pending.load(json) end end end # update with the latest once available fetch(request).then do |response| if response.ok cache.put(request, response.clone()) response.json().then do |json| fetched = true Pending.load(json) end end end end end def self.load(value) Pending.initialize_offline() Server.pending = value if value Main.refresh() return value end def self.count return 0 unless Server.pending and Agenda.file == Server.pending.agenda self.comments.keys().length + self.approved.length + self.unapproved.length + self.flagged.length + self.unflagged.length + self.status.keys().length end def self.comments return {} unless Server.pending and Agenda.file == Server.pending.agenda Server.pending.comments || {} end def self.approved return [] unless Server.pending and Agenda.file == Server.pending.agenda Server.pending.approved || [] end def self.unapproved return [] unless Server.pending and Agenda.file == Server.pending.agenda Server.pending.unapproved || [] end def self.flagged return [] unless Server.pending and Agenda.file == Server.pending.agenda Server.pending.flagged || [] end def self.unflagged return [] unless Server.pending and Agenda.file == Server.pending.agenda Server.pending.unflagged || [] end def self.seen return {} unless Server.pending and Agenda.file == Server.pending.agenda Server.pending.seen || {} end def self.status return [] unless Server.pending and Agenda.file == Server.pending.agenda Server.pending.status || [] end # find a pending status update that matches a given action item def self.find_status(action) return nil unless Server.pending and Agenda.file == Server.pending.agenda match = nil Pending.status.each do |status| found = true action.each_pair do |name, value| found = false if name != 'status' and value != status[name] end match = status if found end return match end # determine if offline operations are (or should be) supported def self.offline_enabled return false unless PageCache.enabled # disable offline in production for now # if location.hostname =~ /^whimsy.*\.apache\.org$/ # return false unless location.hostname.include? '-test' # end return true end # offline storage using IndexDB def self.dbopen(&block) request = indexedDB.open('whimsy/board/agenda', 1) def request.onerror(event) console.log 'pending database not available' end def request.onsuccess(event) block(event.target.result) end def request.onupgradeneeded(event) db = event.target.result db.createObjectStore('pending', keyPath: 'key') end end # fetch pending value. Note: callback block will not be called if there # is no data, or if the data is for another month's agenda def self.dbget(&block) self.dbopen do |db| tx = db.transaction('pending', :readonly) store = tx.objectStore('pending') request = store.get('pending') def request.onerror(event) console.log 'no pending data' end def request.onsuccess(event) if request.result and request.result.agenda == Agenda.date block(request.result.value) else block({}) end end end end # update pending value. def self.dbput(value) self.dbopen do |db| tx = db.transaction('pending', :readwrite) store = tx.objectStore('pending') request = store.put(key: 'pending', agenda: Agenda.date, value: value) def request.onerror(event) console.log 'pending write failed' end end end # change offline status def self.setOffline(status = true) Pending.initialize_offline() localStorage.setItem(Pending.offline_var, status.to_s) Server.offline = (status.to_s == 'true') Main.refresh() event = CustomEvent.new('offlineStatus', detail: Server.offline) window.dispatchEvent(event) end # synchronize offline status with other windows def self.initialize_offline() return if @@offline_initialized Pending.offline_var = "#{JSONStorage.prefix}-offline" if defined? localStorage if localStorage.getItem(Pending.offline_var) == 'true' Server.offline = true end # watch for changes window.addEventListener :storage do |event| if event.key == Pending.offline_var Server.offline = (event.newValue == 'true') event = CustomEvent.new('offlineStatus', detail: Server.offline) window.dispatchEvent(event) end end end if Server.offline # apply offline changes Pending.dbget do |pending| if pending.approve pending.approve.each_pair do |attach, request| Pending.update('approve', attach: attach, request: request) end end end end @@offline_initialized = true end # apply pending update request: if offline, capture request locally, otherwise # post it to the server. def self.update(request, data, &block) if Server.offline Pending.dbget do |pending| if request == 'comment' pending.comment ||= {} pending.comment[data.attach] = data.comment Server.pending.comments[data.attach] = data.comment elsif request == 'approve' # update list of offline requests if data.request.include? 'approve' pending.approve ||= {} pending.approve[data.attach] = data.request elsif data.request.include? 'flag' pending.flag ||= {} pending.flag[data.attach] = data.request end # apply request locally if data.request == 'approve' index = Server.pending.unapproved.indexOf(Server.pending.attach) Server.pending.unapproved.splice(index, 1) if index != -1 unless Server.pending.approved.include? data.attach Server.pending.approved << data.attach end elsif data.request == 'unapprove' index = Server.pending.approved.indexOf(data.attach) Server.pending.approved.splice(index, 1) if index != -1 unless Server.pending.unapproved.include? data.attach Server.pending.unapproved << data.attach end elsif data.request == 'flag' index = Server.pending.unflagged.indexOf(Server.pending.attach) Server.pending.unflagged.splice(index, 1) if index != -1 unless Server.pending.flagged.include? data.attach Server.pending.flagged << data.attach end elsif data.request == 'unflag' index = Server.pending.flagged.indexOf(data.attach) Server.pending.flagged.splice(index, 1) if index != -1 unless Server.pending.unflagged.include? data.attach Server.pending.unflagged << data.attach end end end # store offline requests Pending.dbput pending # inform caller, other tabs if block block(Server.pending) Events.broadcast type: 'pending', value: Server.pending end end else post request, data do |pending| block(pending) Pending.load(pending) end end end end Events.subscribe :pending do |message| Pending.load(message.value) end