sup 0.1 → 0.2
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of sup might be problematic. Click here for more details.
- data/History.txt +9 -0
- data/Manifest.txt +5 -1
- data/Rakefile +2 -1
- data/bin/sup +27 -10
- data/bin/sup-add +2 -1
- data/bin/sup-sync-back +51 -23
- data/doc/FAQ.txt +29 -37
- data/doc/Hooks.txt +38 -0
- data/doc/{UserGuide.txt → NewUserGuide.txt} +27 -21
- data/doc/TODO +91 -57
- data/lib/sup.rb +17 -1
- data/lib/sup/buffer.rb +80 -16
- data/lib/sup/colormap.rb +0 -2
- data/lib/sup/contact.rb +3 -2
- data/lib/sup/crypto.rb +110 -0
- data/lib/sup/draft.rb +2 -6
- data/lib/sup/hook.rb +131 -0
- data/lib/sup/imap.rb +27 -16
- data/lib/sup/index.rb +38 -14
- data/lib/sup/keymap.rb +0 -2
- data/lib/sup/label.rb +30 -9
- data/lib/sup/logger.rb +12 -1
- data/lib/sup/maildir.rb +48 -3
- data/lib/sup/mbox.rb +1 -1
- data/lib/sup/mbox/loader.rb +22 -12
- data/lib/sup/mbox/ssh-loader.rb +1 -1
- data/lib/sup/message-chunks.rb +198 -0
- data/lib/sup/message.rb +154 -115
- data/lib/sup/modes/compose-mode.rb +18 -0
- data/lib/sup/modes/contact-list-mode.rb +1 -1
- data/lib/sup/modes/edit-message-mode.rb +112 -31
- data/lib/sup/modes/file-browser-mode.rb +1 -1
- data/lib/sup/modes/inbox-mode.rb +1 -1
- data/lib/sup/modes/label-list-mode.rb +8 -6
- data/lib/sup/modes/label-search-results-mode.rb +4 -1
- data/lib/sup/modes/log-mode.rb +1 -1
- data/lib/sup/modes/reply-mode.rb +18 -16
- data/lib/sup/modes/search-results-mode.rb +1 -1
- data/lib/sup/modes/thread-index-mode.rb +61 -33
- data/lib/sup/modes/thread-view-mode.rb +111 -102
- data/lib/sup/person.rb +5 -1
- data/lib/sup/poll.rb +36 -7
- data/lib/sup/sent.rb +1 -0
- data/lib/sup/source.rb +7 -3
- data/lib/sup/textfield.rb +48 -34
- data/lib/sup/thread.rb +9 -5
- data/lib/sup/util.rb +16 -22
- metadata +7 -3
@@ -1,6 +1,8 @@
|
|
1
1
|
module Redwood
|
2
2
|
|
3
3
|
class ThreadViewMode < LineCursorMode
|
4
|
+
include CanSpawnComposeMode
|
5
|
+
|
4
6
|
## this holds all info we need to lay out a message
|
5
7
|
class MessageLayout
|
6
8
|
attr_accessor :top, :bot, :prev, :next, :depth, :width, :state, :color, :star_color, :orig_new
|
@@ -14,24 +16,27 @@ class ThreadViewMode < LineCursorMode
|
|
14
16
|
INDENT_SPACES = 2 # how many spaces to indent child messages
|
15
17
|
|
16
18
|
register_keymap do |k|
|
17
|
-
k.add :toggle_detailed_header, "Toggle detailed header", '
|
19
|
+
k.add :toggle_detailed_header, "Toggle detailed header", 'h'
|
18
20
|
k.add :show_header, "Show full message header", 'H'
|
19
|
-
k.add :
|
21
|
+
k.add :activate_chunk, "Expand/collapse or activate item", :enter
|
20
22
|
k.add :expand_all_messages, "Expand/collapse all messages", 'E'
|
21
23
|
k.add :edit_draft, "Edit draft", 'e'
|
24
|
+
k.add :edit_labels, "Edit or add labels for a thread", 'l'
|
22
25
|
k.add :expand_all_quotes, "Expand/collapse all quotes in a message", 'o'
|
23
26
|
k.add :jump_to_next_open, "Jump to next open message", 'n'
|
24
27
|
k.add :jump_to_prev_open, "Jump to previous open message", 'p'
|
25
28
|
k.add :toggle_starred, "Star or unstar message", '*'
|
26
|
-
k.add :
|
29
|
+
k.add :toggle_new, "Toggle new/read status of message", 'N'
|
30
|
+
# k.add :collapse_non_new_messages, "Collapse all but new messages", 'N'
|
27
31
|
k.add :reply, "Reply to a message", 'r'
|
28
32
|
k.add :forward, "Forward a message", 'f'
|
29
|
-
k.add :alias, "Edit alias/nickname for a person", '
|
33
|
+
k.add :alias, "Edit alias/nickname for a person", 'i'
|
30
34
|
k.add :edit_as_new, "Edit message as new", 'D'
|
31
35
|
k.add :save_to_disk, "Save message/attachment to disk", 's'
|
32
36
|
k.add :search, "Search for messages from particular people", 'S'
|
33
37
|
k.add :compose, "Compose message to person", 'm'
|
34
|
-
k.add :archive_and_kill, "Archive thread and kill buffer", '
|
38
|
+
k.add :archive_and_kill, "Archive thread and kill buffer", 'a'
|
39
|
+
k.add :delete_and_kill, "Delete thread and kill buffer", 'd'
|
35
40
|
end
|
36
41
|
|
37
42
|
## there are a couple important instance variables we hold to format
|
@@ -58,7 +63,7 @@ class ThreadViewMode < LineCursorMode
|
|
58
63
|
@layout[m].state = initial_state_for m
|
59
64
|
@layout[m].color = altcolor ? :alternate_patina_color : :message_patina_color
|
60
65
|
@layout[m].star_color = altcolor ? :alternate_starred_patina_color : :starred_patina_color
|
61
|
-
@layout[m].orig_new = m.has_label? :
|
66
|
+
@layout[m].orig_new = m.has_label? :read
|
62
67
|
altcolor = !altcolor
|
63
68
|
if latest_date.nil? || m.date > latest_date
|
64
69
|
latest_date = m.date
|
@@ -69,6 +74,7 @@ class ThreadViewMode < LineCursorMode
|
|
69
74
|
@layout[latest].state = :open if @layout[latest].state == :closed
|
70
75
|
@layout[earliest].state = :detailed if earliest.has_label?(:unread) || @thread.size == 1
|
71
76
|
|
77
|
+
@thread.remove_label :unread
|
72
78
|
regen_text
|
73
79
|
end
|
74
80
|
|
@@ -105,7 +111,7 @@ class ThreadViewMode < LineCursorMode
|
|
105
111
|
m = @message_lines[curpos] or return
|
106
112
|
mode = ForwardMode.new m
|
107
113
|
BufferManager.spawn "Forward of #{m.subj}", mode
|
108
|
-
mode.
|
114
|
+
mode.edit_message
|
109
115
|
end
|
110
116
|
|
111
117
|
include CanAliasContacts
|
@@ -124,66 +130,87 @@ class ThreadViewMode < LineCursorMode
|
|
124
130
|
|
125
131
|
def compose
|
126
132
|
p = @person_lines[curpos]
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
end
|
133
|
-
BufferManager.spawn "Compose message", mode
|
134
|
-
mode.edit
|
133
|
+
if p
|
134
|
+
spawn_compose_mode :to => [p]
|
135
|
+
else
|
136
|
+
spawn_compose_mode
|
137
|
+
end
|
135
138
|
end
|
136
139
|
|
140
|
+
def edit_labels
|
141
|
+
reserved_labels = @thread.labels.select { |l| LabelManager::RESERVED_LABELS.include? l }
|
142
|
+
new_labels = BufferManager.ask_for_labels :label, "Labels for thread: ", @thread.labels
|
143
|
+
|
144
|
+
return unless new_labels
|
145
|
+
@thread.labels = (reserved_labels + new_labels).uniq
|
146
|
+
new_labels.each { |l| LabelManager << l }
|
147
|
+
update
|
148
|
+
UpdateManager.relay self, :label, m
|
149
|
+
end
|
150
|
+
|
137
151
|
def toggle_starred
|
138
152
|
m = @message_lines[curpos] or return
|
139
|
-
|
140
|
-
|
153
|
+
toggle_label m, :starred
|
154
|
+
end
|
155
|
+
|
156
|
+
def toggle_new
|
157
|
+
m = @message_lines[curpos] or return
|
158
|
+
toggle_label m, :unread
|
159
|
+
end
|
160
|
+
|
161
|
+
def toggle_label m, label
|
162
|
+
if m.has_label? label
|
163
|
+
m.remove_label label
|
141
164
|
else
|
142
|
-
m.add_label
|
165
|
+
m.add_label label
|
143
166
|
end
|
144
167
|
## TODO: don't recalculate EVERYTHING just to add a stupid little
|
145
168
|
## star to the display
|
146
169
|
update
|
147
|
-
UpdateManager.relay self, :
|
170
|
+
UpdateManager.relay self, :label, m
|
148
171
|
end
|
149
172
|
|
150
|
-
|
173
|
+
## called when someone presses enter when the cursor is highlighting
|
174
|
+
## a chunk. for expandable chunks (including messages) we toggle
|
175
|
+
## open/closed state; for viewable chunks (like attachments) we
|
176
|
+
## view.
|
177
|
+
def activate_chunk
|
151
178
|
chunk = @chunk_lines[curpos] or return
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
when Message::Quote, Message::Signature
|
158
|
-
return if chunk.lines.length == 1
|
159
|
-
toggle_chunk_expansion chunk
|
160
|
-
when Message::Attachment
|
161
|
-
if chunk.inlineable?
|
162
|
-
toggle_chunk_expansion chunk
|
163
|
-
else
|
164
|
-
view_attachment chunk
|
179
|
+
layout =
|
180
|
+
if chunk.is_a?(Message)
|
181
|
+
@layout[chunk]
|
182
|
+
elsif chunk.expandable?
|
183
|
+
@chunk_layout[chunk]
|
165
184
|
end
|
185
|
+
if layout
|
186
|
+
layout.state = (layout.state != :closed ? :closed : :open)
|
187
|
+
#cursor_down if layout.state == :closed # too annoying
|
188
|
+
update
|
189
|
+
elsif chunk.viewable?
|
190
|
+
view chunk
|
166
191
|
end
|
167
|
-
update
|
168
192
|
end
|
169
193
|
|
170
194
|
def edit_as_new
|
171
195
|
m = @message_lines[curpos] or return
|
172
196
|
mode = ComposeMode.new(:body => m.basic_body_lines, :to => m.to, :cc => m.cc, :subj => m.subj, :bcc => m.bcc)
|
173
197
|
BufferManager.spawn "edit as new", mode
|
174
|
-
mode.
|
198
|
+
mode.edit_message
|
175
199
|
end
|
176
200
|
|
177
201
|
def save_to_disk
|
178
202
|
chunk = @chunk_lines[curpos] or return
|
179
203
|
case chunk
|
180
|
-
when
|
181
|
-
fn = BufferManager.
|
204
|
+
when Chunk::Attachment
|
205
|
+
fn = BufferManager.ask_for_filename :filename, "Save attachment to file: ", chunk.filename
|
182
206
|
save_to_file(fn) { |f| f.print chunk.raw_content } if fn
|
183
207
|
else
|
184
208
|
m = @message_lines[curpos]
|
185
|
-
fn = BufferManager.
|
186
|
-
|
209
|
+
fn = BufferManager.ask_for_filename :filename, "Save message to file: "
|
210
|
+
return unless fn
|
211
|
+
save_to_file(fn) do |f|
|
212
|
+
m.each_raw_message_line { |l| f.print l }
|
213
|
+
end
|
187
214
|
end
|
188
215
|
end
|
189
216
|
|
@@ -193,7 +220,7 @@ class ThreadViewMode < LineCursorMode
|
|
193
220
|
mode = ResumeMode.new m
|
194
221
|
BufferManager.spawn "Edit message", mode
|
195
222
|
BufferManager.kill_buffer self.buffer
|
196
|
-
mode.
|
223
|
+
mode.edit_message
|
197
224
|
else
|
198
225
|
BufferManager.flash "Not a draft message!"
|
199
226
|
end
|
@@ -263,7 +290,7 @@ class ThreadViewMode < LineCursorMode
|
|
263
290
|
|
264
291
|
def expand_all_quotes
|
265
292
|
if(m = @message_lines[curpos])
|
266
|
-
quotes = m.chunks.select { |c| (c.is_a?(
|
293
|
+
quotes = m.chunks.select { |c| (c.is_a?(Chunk::Quote) || c.is_a?(Chunk::Signature)) && c.lines.length > 1 }
|
267
294
|
numopen = quotes.inject(0) { |s, c| s + (@chunk_layout[c].state == :open ? 1 : 0) }
|
268
295
|
newstate = numopen > quotes.length / 2 ? :closed : :open
|
269
296
|
quotes.each { |c| @chunk_layout[c].state = newstate }
|
@@ -281,14 +308,14 @@ class ThreadViewMode < LineCursorMode
|
|
281
308
|
BufferManager.kill_buffer_safely buffer
|
282
309
|
end
|
283
310
|
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
l.state = (l.state != :closed ? :closed : :open)
|
289
|
-
cursor_down if l.state == :closed
|
311
|
+
def delete_and_kill
|
312
|
+
@thread.apply_label :deleted
|
313
|
+
UpdateManager.relay self, :deleted, @thread
|
314
|
+
BufferManager.kill_buffer_safely buffer
|
290
315
|
end
|
291
316
|
|
317
|
+
private
|
318
|
+
|
292
319
|
def initial_state_for m
|
293
320
|
if m.has_label?(:starred) || m.has_label?(:unread)
|
294
321
|
:open
|
@@ -348,8 +375,8 @@ private
|
|
348
375
|
|
349
376
|
## set the default state for chunks
|
350
377
|
cl.state ||=
|
351
|
-
if c.
|
352
|
-
|
378
|
+
if c.expandable? && c.respond_to?(:initial_state)
|
379
|
+
c.initial_state
|
353
380
|
else
|
354
381
|
:closed
|
355
382
|
end
|
@@ -370,14 +397,10 @@ private
|
|
370
397
|
|
371
398
|
def message_patina_lines m, state, start, parent, prefix, color, star_color
|
372
399
|
prefix_widget = [color, prefix]
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
when :open, :detailed
|
378
|
-
[color, "- "]
|
379
|
-
end
|
380
|
-
imp_widget =
|
400
|
+
|
401
|
+
open_widget = [color, (state == :closed ? "+ " : "- ")]
|
402
|
+
new_widget = [color, (m.has_label?(:unread) ? "N" : " ")]
|
403
|
+
starred_widget =
|
381
404
|
if m.has_label?(:starred)
|
382
405
|
[star_color, "* "]
|
383
406
|
else
|
@@ -387,39 +410,41 @@ private
|
|
387
410
|
case state
|
388
411
|
when :open
|
389
412
|
@person_lines[start] = m.from
|
390
|
-
[[prefix_widget,
|
413
|
+
[[prefix_widget, open_widget, new_widget, starred_widget,
|
391
414
|
[color,
|
392
415
|
"#{m.from ? m.from.mediumname : '?'} to #{m.recipients.map { |l| l.shortname }.join(', ')} #{m.date.to_nice_s} (#{m.date.to_nice_distance_s})"]]]
|
393
416
|
|
394
417
|
when :closed
|
395
418
|
@person_lines[start] = m.from
|
396
|
-
[[prefix_widget,
|
419
|
+
[[prefix_widget, open_widget, new_widget, starred_widget,
|
397
420
|
[color,
|
398
421
|
"#{m.from ? m.from.mediumname : '?'}, #{m.date.to_nice_s} (#{m.date.to_nice_distance_s}) #{m.snippet}"]]]
|
399
422
|
|
400
423
|
when :detailed
|
401
424
|
@person_lines[start] = m.from
|
402
|
-
from = [[prefix_widget,
|
425
|
+
from = [[prefix_widget, open_widget, new_widget, starred_widget,
|
426
|
+
[color, "From: #{m.from ? format_person(m.from) : '?'}"]]]
|
403
427
|
|
404
428
|
rest = []
|
405
429
|
unless m.to.empty?
|
406
430
|
m.to.each_with_index { |p, i| @person_lines[start + rest.length + from.length + i] = p }
|
407
|
-
rest += format_person_list "
|
431
|
+
rest += format_person_list " To: ", m.to
|
408
432
|
end
|
409
433
|
unless m.cc.empty?
|
410
434
|
m.cc.each_with_index { |p, i| @person_lines[start + rest.length + from.length + i] = p }
|
411
|
-
rest += format_person_list "
|
435
|
+
rest += format_person_list " Cc: ", m.cc
|
412
436
|
end
|
413
437
|
unless m.bcc.empty?
|
414
438
|
m.bcc.each_with_index { |p, i| @person_lines[start + rest.length + from.length + i] = p }
|
415
|
-
rest += format_person_list "
|
439
|
+
rest += format_person_list " Bcc: ", m.bcc
|
416
440
|
end
|
417
441
|
|
442
|
+
show_labels = @thread.labels - LabelManager::HIDDEN_RESERVED_LABELS
|
418
443
|
rest += [
|
419
|
-
"
|
420
|
-
"
|
421
|
-
(parent ? "
|
422
|
-
|
444
|
+
" Date: #{m.date.strftime DATE_FORMAT} (#{m.date.to_nice_distance_s})",
|
445
|
+
" Subject: #{m.subj}",
|
446
|
+
(parent ? " In reply to: #{parent.from.mediumname}'s message of #{parent.date.strftime DATE_FORMAT}" : nil),
|
447
|
+
show_labels.empty? ? nil : " Labels: #{show_labels.join(', ')}",
|
423
448
|
].compact
|
424
449
|
|
425
450
|
from + rest.map { |l| [[color, prefix + " " + l]] }
|
@@ -439,6 +464,7 @@ private
|
|
439
464
|
p.longname + (ContactManager.is_contact?(p) ? " (#{ContactManager.alias_for p})" : "")
|
440
465
|
end
|
441
466
|
|
467
|
+
## todo: check arguments on this overly complex function
|
442
468
|
def chunk_to_lines chunk, state, start, depth, parent=nil, color=nil, star_color=nil
|
443
469
|
prefix = " " * INDENT_SPACES * depth
|
444
470
|
case chunk
|
@@ -449,48 +475,31 @@ private
|
|
449
475
|
when Message
|
450
476
|
message_patina_lines(chunk, state, start, parent, prefix, color, star_color) +
|
451
477
|
(chunk.is_draft? ? [[[:draft_notification_color, prefix + " >>> This message is a draft. To edit, hit 'e'. <<<"]]] : [])
|
452
|
-
|
453
|
-
return [[[:attachment_color, "#{prefix}x Attachment: #{chunk.filename} (#{chunk.content_type})"]]] unless chunk.inlineable?
|
454
|
-
case state
|
455
|
-
when :closed
|
456
|
-
[[[:attachment_color, "#{prefix}+ Attachment: #{chunk.filename} (#{chunk.lines.length} lines)"]]]
|
457
|
-
when :open
|
458
|
-
[[[:attachment_color, "#{prefix}- Attachment: #{chunk.filename} (#{chunk.lines.length} lines)"]]] + chunk.lines.map { |line| [[:none, "#{prefix}#{line}"]] }
|
459
|
-
end
|
460
|
-
when Message::Text
|
461
|
-
t = chunk.lines
|
462
|
-
if t.last =~ /^\s*$/ && t.length > 1
|
463
|
-
t.pop while t[-2] =~ /^\s*$/ # pop until only one file empty line
|
464
|
-
end
|
465
|
-
t.map { |line| [[:none, "#{prefix}#{line}"]] }
|
466
|
-
when Message::Quote
|
467
|
-
return [[[:quote_color, "#{prefix}#{chunk.lines.first}"]]] if chunk.lines.length == 1
|
468
|
-
case state
|
469
|
-
when :closed
|
470
|
-
[[[:quote_patina_color, "#{prefix}+ (#{chunk.lines.length} quoted lines)"]]]
|
471
|
-
when :open
|
472
|
-
[[[:quote_patina_color, "#{prefix}- (#{chunk.lines.length} quoted lines)"]]] + chunk.lines.map { |line| [[:quote_color, "#{prefix}#{line}"]] }
|
473
|
-
end
|
474
|
-
when Message::Signature
|
475
|
-
return [[[:sig_patina_color, "#{prefix}#{chunk.lines.first}"]]] if chunk.lines.length == 1
|
476
|
-
case state
|
477
|
-
when :closed
|
478
|
-
[[[:sig_patina_color, "#{prefix}+ (#{chunk.lines.length}-line signature)"]]]
|
479
|
-
when :open
|
480
|
-
[[[:sig_patina_color, "#{prefix}- (#{chunk.lines.length}-line signature)"]]] + chunk.lines.map { |line| [[:sig_color, "#{prefix}#{line}"]] }
|
481
|
-
end
|
478
|
+
|
482
479
|
else
|
483
|
-
raise "
|
480
|
+
raise "Bad chunk: #{chunk.inspect}" unless chunk.respond_to?(:inlineable?) ## debugging
|
481
|
+
if chunk.inlineable?
|
482
|
+
chunk.lines.map { |line| [[chunk.color, "#{prefix}#{line}"]] }
|
483
|
+
elsif chunk.expandable?
|
484
|
+
case state
|
485
|
+
when :closed
|
486
|
+
[[[chunk.patina_color, "#{prefix}+ #{chunk.patina_text}"]]]
|
487
|
+
when :open
|
488
|
+
[[[chunk.patina_color, "#{prefix}- #{chunk.patina_text}"]]] + chunk.lines.map { |line| [[chunk.color, "#{prefix}#{line}"]] }
|
489
|
+
end
|
490
|
+
else
|
491
|
+
[[[chunk.patina_color, "#{prefix}x #{chunk.patina_text}"]]]
|
492
|
+
end
|
484
493
|
end
|
485
494
|
end
|
486
495
|
|
487
|
-
def
|
488
|
-
BufferManager.flash "viewing #{
|
489
|
-
success =
|
496
|
+
def view chunk
|
497
|
+
BufferManager.flash "viewing #{chunk.content_type} attachment..."
|
498
|
+
success = chunk.view!
|
490
499
|
BufferManager.erase_flash
|
491
500
|
BufferManager.completely_redraw_screen
|
492
501
|
unless success
|
493
|
-
BufferManager.spawn "Attachment: #{
|
502
|
+
BufferManager.spawn "Attachment: #{chunk.filename}", TextMode.new(chunk.to_s)
|
494
503
|
BufferManager.flash "Couldn't execute view command, viewing as text."
|
495
504
|
end
|
496
505
|
end
|
data/lib/sup/person.rb
CHANGED
@@ -20,6 +20,7 @@ class PersonManager
|
|
20
20
|
def save
|
21
21
|
File.open(@fn, "w") do |f|
|
22
22
|
@@people.each do |email, p|
|
23
|
+
next if p.email == p.name
|
23
24
|
f.puts "#{p.email}: #{p.timestamp} #{p.name}"
|
24
25
|
end
|
25
26
|
end
|
@@ -114,7 +115,7 @@ class Person
|
|
114
115
|
|
115
116
|
def full_address
|
116
117
|
if @name && @email
|
117
|
-
if @name =~ /"/
|
118
|
+
if @name =~ /[",@]/
|
118
119
|
"#{@name.inspect} <#@email>" # escape quotes
|
119
120
|
else
|
120
121
|
"#@name <#@email>"
|
@@ -159,6 +160,9 @@ class Person
|
|
159
160
|
|
160
161
|
Person.new name, email
|
161
162
|
end
|
163
|
+
|
164
|
+
def eql? o; email.eql? o.email end
|
165
|
+
def hash; email.hash end
|
162
166
|
end
|
163
167
|
|
164
168
|
end
|
data/lib/sup/poll.rb
CHANGED
@@ -5,12 +5,29 @@ module Redwood
|
|
5
5
|
class PollManager
|
6
6
|
include Singleton
|
7
7
|
|
8
|
+
HookManager.register "before-poll", <<EOS
|
9
|
+
Executes immediately before a poll for new messages commences.
|
10
|
+
No variables.
|
11
|
+
EOS
|
12
|
+
|
13
|
+
HookManager.register "after-poll", <<EOS
|
14
|
+
Executes immediately after a poll for new messages completes.
|
15
|
+
Variables:
|
16
|
+
num: the total number of new messages
|
17
|
+
num_inbox: the number of new messages appearing in the inbox (i.e.
|
18
|
+
not auto-archived).
|
19
|
+
from_and_subj: an array of (from email address, subject) pairs
|
20
|
+
from_and_subj_inbox: an array of (from email address, subject) pairs for
|
21
|
+
messages appearing in the inbox
|
22
|
+
EOS
|
23
|
+
|
8
24
|
DELAY = 300
|
9
25
|
|
10
26
|
def initialize
|
11
27
|
@mutex = Mutex.new
|
12
28
|
@thread = nil
|
13
29
|
@last_poll = nil
|
30
|
+
@polling = false
|
14
31
|
|
15
32
|
self.class.i_am_the_instance self
|
16
33
|
end
|
@@ -20,13 +37,21 @@ class PollManager
|
|
20
37
|
end
|
21
38
|
|
22
39
|
def poll
|
40
|
+
return if @polling
|
41
|
+
@polling = true
|
42
|
+
HookManager.run "before-poll"
|
43
|
+
|
23
44
|
BufferManager.flash "Polling for new messages..."
|
24
|
-
num, numi = buffer.mode.poll
|
45
|
+
num, numi, from_and_subj, from_and_subj_inbox = buffer.mode.poll
|
25
46
|
if num > 0
|
26
47
|
BufferManager.flash "Loaded #{num} new messages, #{numi} to inbox."
|
27
48
|
else
|
28
49
|
BufferManager.flash "No new messages."
|
29
50
|
end
|
51
|
+
|
52
|
+
HookManager.run "after-poll", :num => num, :num_inbox => numi, :from_and_subj => from_and_subj, :from_and_subj_inbox => from_and_subj_inbox
|
53
|
+
|
54
|
+
@polling = false
|
30
55
|
[num, numi]
|
31
56
|
end
|
32
57
|
|
@@ -46,6 +71,9 @@ class PollManager
|
|
46
71
|
|
47
72
|
def do_poll
|
48
73
|
total_num = total_numi = 0
|
74
|
+
from_and_subj = []
|
75
|
+
from_and_subj_inbox = []
|
76
|
+
|
49
77
|
@mutex.synchronize do
|
50
78
|
Index.usual_sources.each do |source|
|
51
79
|
# yield "source #{source} is done? #{source.done?} (cur_offset #{source.cur_offset} >= #{source.end_offset})"
|
@@ -65,11 +93,15 @@ class PollManager
|
|
65
93
|
yield "Found message at #{offset} with labels {#{m.labels * ', '}}"
|
66
94
|
unless entry
|
67
95
|
num += 1
|
68
|
-
|
96
|
+
from_and_subj << [m.from.longname, m.subj]
|
97
|
+
if m.labels.include? :inbox
|
98
|
+
from_and_subj_inbox << [m.from.longname, m.subj]
|
99
|
+
numi += 1
|
100
|
+
end
|
69
101
|
end
|
70
102
|
m
|
71
103
|
end
|
72
|
-
yield "Found #{num} messages, #{numi} to inbox" unless num == 0
|
104
|
+
yield "Found #{num} messages, #{numi} to inbox." unless num == 0
|
73
105
|
total_num += num
|
74
106
|
total_numi += numi
|
75
107
|
end
|
@@ -78,7 +110,7 @@ class PollManager
|
|
78
110
|
@last_poll = Time.now
|
79
111
|
@polling = false
|
80
112
|
end
|
81
|
-
[total_num, total_numi]
|
113
|
+
[total_num, total_numi, from_and_subj, from_and_subj_inbox]
|
82
114
|
end
|
83
115
|
|
84
116
|
## this is the main mechanism for adding new messages to the
|
@@ -112,9 +144,6 @@ class PollManager
|
|
112
144
|
if m.source_marked_read?
|
113
145
|
m.remove_label :unread
|
114
146
|
labels.delete :unread
|
115
|
-
else
|
116
|
-
m.add_label :unread
|
117
|
-
labels << :unread
|
118
147
|
end
|
119
148
|
|
120
149
|
docid, entry = Index.load_entry_for_id m.id
|