www/secretary/workbench/views/index.js.rb (263 lines of code) (raw):

# # Index page showing unprocessed messages with attachments # class Index < Vue def log(msg) console.log msg end def initialize() log 'initialize' @selected = nil @messages = [] @checking = false @fetched = false end # crop a value before display def _crop(text, max) if text and text.length > max return text[0..max] + ' ...' end return text end def render() log 'render' if not @messages or @messages.all? {|message| message.status == :deleted} _p.container_fluid 'All documents have been processed.' else _table.table do _thead do _tr do _th 'Date' _th 'From' _th 'Subject' end end _tbody do @messages.each do |message| # determine the 'color' to use for the row color = nil color = 'deleted' if message.status == :deletePending color = 'hidden' if message.status == :deleted color = 'selected' if message.href == @selected row_options = { :class => color, on: {click: self.selectRow, doubleClick: self.nav} } _tr row_options do _td do if %i[emeritusReady emeritusPending withdrawalReady withdrawalPending].include? message.status _a message.date, href: message.href, title: message.time, target: '_blank' else _a message.date, href: message.href, title: message.time end end _td _crop(message.from, 40) if %i[emeritusReady emeritusPending withdrawalReady withdrawalPending].include? message.status _td do _a message.subject, class: message.status, href: message.href2, target: '_blank' end elsif message.secmail _td do _ message.subject _ ' - ' _b message.secmail.inspect # TODO better presentation of content end else _td _crop(message.subject, 60) end end end end end end if @fetched and @nextmbox _button.btn.btn_primary 'download previous month', onClick: self.fetch_month end if defined? window unless window.location.hostname =~ /^whimsy.*\.apache\.org$/ _button.btn.btn_success 'check for new mail', onClick: self.refresh, disabled: @checking end end unless Status.undoStack.empty? _button.btn.btn_info 'undo delete', onClick: self.undo end end # initialize next mailbox (year+month) def beforeMount() @nextmbox = @@mbox if @@messages log 'beforeMount - messages' else log 'beforeMount - nomessages' end self.mergemsgs @@messages if @@messages # temp debug self.merge @@messages if @@messages merge @@messages if @@messages end # on initial load, fetch latest mailbox, subscribe to keyboard and # server side events, and initialize selected item. def mounted() today = Date.new() twice = (today.getMonth()+1==@nextmbox[4..5].to_i and today.getDate()<=7) log "mounted twice=#{twice} next=#{@nextmbox}" self.fetch_month() do if @nextmbox and twice # for the first week of the month, fetch previous month too self.fetch_month() do @fetched = true end else @fetched = true end end window.onkeydown = self.keydown # when events are received, update messages events = EventSource.new('events') events.addEventListener :message do |event| messages = JSON.parse(event.data).messages self.mergemsgs messages if messages end # close connection on exit window.addEventListener :unload do |event| events.close() end # select row self.selectRow Status.selected if @messages.length > 0 end # when content changes, ensure selected message is visible def updated() log 'updated' if @selected selected = document.querySelector("a[href='#{@selected}']") if selected rect = selected.getBoundingClientRect() if rect.top < 0 or rect.left < 0 or rect.bottom > window.innerHeight or rect.right > window.innerWidth then selected.scrollIntoView() end end end end # fetch a month's worth of messages def fetch_month(&block) log "fetch_month #{@nextmbox}" HTTP.get(@nextmbox, :json).then {|response| # update latest mbox @nextmbox = response.mbox # add messages to list self.mergemsgs response.messages # select oldest message self.selectRow Status.selected || @messages.last unless @selected # if block provided, call it block() if block and block.is_a? Function }.catch {|error| console.log error alert error } end def merge(messages) log 'merge()' end # merge new messages into the list def mergemsgs(messages) log "mergemsgs #{messages.length}" messages.each do |new_message| index = @messages.find_index do |old_message| old_message.time < new_message.time or (old_message.time == new_message.time and old_message.href <= new_message.href) end if index == -1 @messages << new_message elsif @messages[index].href == new_message.href @messages[index] = new_message else @messages.splice index, 0, new_message end end Vue.forceUpdate() unless messages.empty? end # update @selected, given either a DOM event or a message def selectRow(object) if not object href = nil elsif typeof(object) == 'string' href = object elsif object.respond_to? :currentTarget href = object.currentTarget.querySelector('a').getAttribute('href') elsif object.respond_to? :href href = object.href else href = object end # ensure selected message is not deleted index = @messages.find_index {|m| m.href == href} index -= 1 while index >= 0 and @messages[index].status == :deleted index = @messages.find_index {|m| m.status != :deleted} if index == -1 @selected = Status.selected = (index >= 0 ? @messages[index].href : nil) end # navigate def nav(event) self.selectRow(event) window.location.href = @selected window.getSelection().removeAllRanges() event.preventDefault() end def undo(event) message = Status.popStack() selected = @messages.find {|m| m.href == message} if selected self.selectRow selected selected.status = :deletePending # send request to server to remove delete status HTTP.patch(selected.href, status: nil).then { delete selected.status Vue.forceUpdate() self.selectRow message }.catch {|error| alert error } end end def refresh(event) log "refresh #{@@mbox}" @checking = true HTTP.post('actions/check-mail', mbox: @@mbox).then {|response| self.mergemsgs response.messages @checking = false }.catch {|error| alert error @checking = false } end # handle keyboard events def keydown(event) if event.keyCode == 38 # up index = @messages.find_index {|m| m.href == @selected} self.selectRow @messages[index-1] if index > 0 event.preventDefault() elsif event.keyCode == 40 # down index = @messages.find_index {|m| m.href == @selected} + 1 while index < @messages.length and @messages[index].status == :deleted index += 1 end self.selectRow @messages[index] if index < @messages.length event.preventDefault() elsif event.keyCode == 13 or event.keyCode == 39 # enter/return or right selected = @messages.find {|m| m.href == @selected} window.location.href = selected.href if selected elsif event.keyCode == 8 or event.keyCode == 46 # backspace or delete if event.metaKey or event.ctrlKey event.preventDefault() # mark item as delete pending selected = @selected index = @messages.find_index {|m| m.href == selected} @messages[index].status = :deletePending if index >= 0 # move selected pointer if index > 0 self.selectRow @messages[index-1] elsif index < @messages.length - 1 self.selectRow @messages[index+1] else self.selectRow nil end # send request to server to perform delete HTTP.delete(selected).then { index = @messages.find_index {|m| m.href == selected} @messages[index].status = :deleted if index >= 0 Status.pushDeleted selected self.selectRow selected if @selected == selected Vue.forceUpdate() }.catch {|error| alert error } end elsif event.keyCode == 'Z'.ord if event.ctrlKey or event.metaKey unless Status.undoStack.empty? self.undo() event.preventDefault() end end else console.log "keydown: #{event.keyCode}" end end end