blob: fe1abe1dd82736279ebc9cae3560cdd616011a6c [file] [log] [blame]
#
# 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
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 'Timestamp'
_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
time = Date.new(Date.parse(message.time)).toLocaleString()
row_options = {
:class => color,
on: {click: self.selectRow, doubleClick: self.nav}
}
_tr row_options do
_td do
if[:emeritusReady, :emeritusPending].include? message.status
_a time, href: "#{message.href}", title: message.time, target: "_blank"
else
_a time, href: "#{message.href}", title: message.time
end
end
_td message.from
_td message.subject
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