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.

Files changed (48) hide show
  1. data/History.txt +9 -0
  2. data/Manifest.txt +5 -1
  3. data/Rakefile +2 -1
  4. data/bin/sup +27 -10
  5. data/bin/sup-add +2 -1
  6. data/bin/sup-sync-back +51 -23
  7. data/doc/FAQ.txt +29 -37
  8. data/doc/Hooks.txt +38 -0
  9. data/doc/{UserGuide.txt → NewUserGuide.txt} +27 -21
  10. data/doc/TODO +91 -57
  11. data/lib/sup.rb +17 -1
  12. data/lib/sup/buffer.rb +80 -16
  13. data/lib/sup/colormap.rb +0 -2
  14. data/lib/sup/contact.rb +3 -2
  15. data/lib/sup/crypto.rb +110 -0
  16. data/lib/sup/draft.rb +2 -6
  17. data/lib/sup/hook.rb +131 -0
  18. data/lib/sup/imap.rb +27 -16
  19. data/lib/sup/index.rb +38 -14
  20. data/lib/sup/keymap.rb +0 -2
  21. data/lib/sup/label.rb +30 -9
  22. data/lib/sup/logger.rb +12 -1
  23. data/lib/sup/maildir.rb +48 -3
  24. data/lib/sup/mbox.rb +1 -1
  25. data/lib/sup/mbox/loader.rb +22 -12
  26. data/lib/sup/mbox/ssh-loader.rb +1 -1
  27. data/lib/sup/message-chunks.rb +198 -0
  28. data/lib/sup/message.rb +154 -115
  29. data/lib/sup/modes/compose-mode.rb +18 -0
  30. data/lib/sup/modes/contact-list-mode.rb +1 -1
  31. data/lib/sup/modes/edit-message-mode.rb +112 -31
  32. data/lib/sup/modes/file-browser-mode.rb +1 -1
  33. data/lib/sup/modes/inbox-mode.rb +1 -1
  34. data/lib/sup/modes/label-list-mode.rb +8 -6
  35. data/lib/sup/modes/label-search-results-mode.rb +4 -1
  36. data/lib/sup/modes/log-mode.rb +1 -1
  37. data/lib/sup/modes/reply-mode.rb +18 -16
  38. data/lib/sup/modes/search-results-mode.rb +1 -1
  39. data/lib/sup/modes/thread-index-mode.rb +61 -33
  40. data/lib/sup/modes/thread-view-mode.rb +111 -102
  41. data/lib/sup/person.rb +5 -1
  42. data/lib/sup/poll.rb +36 -7
  43. data/lib/sup/sent.rb +1 -0
  44. data/lib/sup/source.rb +7 -3
  45. data/lib/sup/textfield.rb +48 -34
  46. data/lib/sup/thread.rb +9 -5
  47. data/lib/sup/util.rb +16 -22
  48. 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", 'd'
19
+ k.add :toggle_detailed_header, "Toggle detailed header", 'h'
18
20
  k.add :show_header, "Show full message header", 'H'
19
- k.add :toggle_expanded, "Expand/collapse item", :enter
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 :collapse_non_new_messages, "Collapse all but new messages", 'N'
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", 'a'
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", 'A'
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? :unread
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.edit
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
- mode =
128
- if p
129
- ComposeMode.new :to => [p]
130
- else
131
- ComposeMode.new
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
- if m.has_label? :starred
140
- m.remove_label :starred
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 :starred
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, :starred, m
170
+ UpdateManager.relay self, :label, m
148
171
  end
149
172
 
150
- def toggle_expanded
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
- case chunk
153
- when Message
154
- l = @layout[chunk]
155
- l.state = (l.state != :closed ? :closed : :open)
156
- cursor_down if l.state == :closed
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.edit
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 Message::Attachment
181
- fn = BufferManager.ask :filename, "Save attachment to file: ", chunk.filename
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.ask :filename, "Save message to file: "
186
- save_to_file(fn) { |f| f.print m.raw_full_message } if fn
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.edit
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?(Message::Quote) || c.is_a?(Message::Signature)) && c.lines.length > 1 }
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
- private
285
-
286
- def toggle_chunk_expansion chunk
287
- l = @chunk_layout[chunk]
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.is_a?(Message::Attachment) && c.inlineable?
352
- :open
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
- widget =
374
- case state
375
- when :closed
376
- [color, "+ "]
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, widget, imp_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, widget, imp_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, widget, imp_widget, [color, "From: #{m.from ? format_person(m.from) : '?'}"]]]
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 " To: ", m.to
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 " Cc: ", m.cc
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 " Bcc: ", m.bcc
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
- " Date: #{m.date.strftime DATE_FORMAT} (#{m.date.to_nice_distance_s})",
420
- " Subject: #{m.subj}",
421
- (parent ? " In reply to: #{parent.from.mediumname}'s message of #{parent.date.strftime DATE_FORMAT}" : nil),
422
- m.labels.empty? ? nil : " Labels: #{m.labels.join(', ')}",
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
- when Message::Attachment
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 "unknown chunk type #{chunk.class.name}"
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 view_attachment a
488
- BufferManager.flash "viewing #{a.content_type} attachment..."
489
- success = a.view!
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: #{a.filename}", TextMode.new(a.to_s)
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
@@ -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
@@ -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
- numi += 1 if m.labels.include? :inbox
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