lib/sup/modes/thread-index-mode.rb in sup-0.9.1 vs lib/sup/modes/thread-index-mode.rb in sup-0.10

- old
+ new

@@ -35,11 +35,11 @@ k.add :edit_labels, "Edit or add labels for a thread", 'l' k.add :edit_message, "Edit message (drafts only)", 'e' k.add :toggle_spam, "Mark/unmark thread as spam", 'S' k.add :toggle_deleted, "Delete/undelete thread", 'd' k.add :kill, "Kill thread (never to be seen in inbox again)", '&' - k.add :save, "Save changes now", '$' + k.add :flush_index, "Flush all changes now", '$' k.add :jump_to_next_new, "Jump to next new thread", :tab k.add :reply, "Reply to latest message in a thread", 'r' k.add :reply_all, "Reply to all participants of the latest message in a thread", 'G' k.add :forward, "Forward latest message in a thread", 'f' k.add :toggle_tagged, "Tag/untag selected thread", 't' @@ -64,11 +64,11 @@ @load_thread_opts = load_thread_opts @hidden_labels = hidden_labels + LabelManager::HIDDEN_RESERVED_LABELS @date_width = DATE_WIDTH @interrupt_search = false - + initialize_threads # defines @ts and @ts_mutex update # defines @text and @lines UpdateManager.register self @@ -218,16 +218,18 @@ def undo UndoManager.undo end def update + old_cursor_thread = cursor_thread @mutex.synchronize do ## let's see you do THIS in python @threads = @ts.threads.select { |t| !@hidden_threads[t] }.sort_by { |t| [t.date, t.first.id] }.reverse @size_widgets = @threads.map { |t| size_widget_for_thread t } @size_widget_width = @size_widgets.max_of { |w| w.display_length } end + set_cursor_pos @threads.index(old_cursor_thread)||curpos regen_text end def edit_message @@ -264,19 +266,22 @@ end def toggle_starred t = cursor_thread or return undo = actually_toggle_starred t - UndoManager.register "toggling thread starred status", undo + UndoManager.register "toggling thread starred status", undo, lambda { Index.save_thread t } update_text_for_line curpos cursor_down + Index.save_thread t end def multi_toggle_starred threads UndoManager.register "toggling #{threads.size.pluralize 'thread'} starred status", - threads.map { |t| actually_toggle_starred t } + threads.map { |t| actually_toggle_starred t }, + lambda { threads.each { |t| Index.save_thread t } } regen_text + threads.each { |t| Index.save_thread t } end ## returns an undo lambda def actually_toggle_archived t thread = t @@ -348,30 +353,36 @@ end def toggle_archived t = cursor_thread or return undo = actually_toggle_archived t - UndoManager.register "deleting/undeleting thread #{t.first.id}", undo, lambda { update_text_for_line curpos } + UndoManager.register "deleting/undeleting thread #{t.first.id}", undo, lambda { update_text_for_line curpos }, + lambda { Index.save_thread t } update_text_for_line curpos + Index.save_thread t end def multi_toggle_archived threads undos = threads.map { |t| actually_toggle_archived t } - UndoManager.register "deleting/undeleting #{threads.size.pluralize 'thread'}", undos, lambda { regen_text } + UndoManager.register "deleting/undeleting #{threads.size.pluralize 'thread'}", undos, lambda { regen_text }, + lambda { threads.each { |t| Index.save_thread t } } regen_text + threads.each { |t| Index.save_thread t } end def toggle_new t = cursor_thread or return t.toggle_label :unread update_text_for_line curpos cursor_down + Index.save_thread t end def multi_toggle_new threads threads.each { |t| t.toggle_label :unread } regen_text + threads.each { |t| Index.save_thread t } end def multi_toggle_tagged threads @mutex.synchronize { @tags.drop_all_tags } regen_text @@ -383,10 +394,11 @@ @tags.apply_to_tagged :join_threads end def multi_join_threads threads @ts.join_threads threads or return + threads.each { |t| Index.save_thread t } @tags.drop_all_tags # otherwise we have tag pointers to invalid threads! update end def jump_to_next_new @@ -397,18 +409,17 @@ if n ## jump there if necessary jump_to_line n unless n >= topline && n < botline set_cursor_pos n else - BufferManager.flash "No new messages" + BufferManager.flash "No new messages." end end def toggle_spam t = cursor_thread or return multi_toggle_spam [t] - HookManager.run("mark-as-spam", :thread => t) end ## both spam and deleted have the curious characteristic that you ## always want to hide the thread after either applying or removing ## that label. in all thread-index-views except for @@ -416,13 +427,15 @@ ## deleted, you want it to disappear immediately; in LSRM, you only ## see deleted or spam emails, and when you undelete or unspam them ## you also want them to disappear immediately. def multi_toggle_spam threads undos = threads.map { |t| actually_toggle_spammed t } + threads.each { |t| HookManager.run("mark-as-spam", :thread => t) } UndoManager.register "marking/unmarking #{threads.size.pluralize 'thread'} as spam", - undos, lambda { regen_text } + undos, lambda { regen_text }, lambda { threads.each { |t| Index.save_thread t } } regen_text + threads.each { |t| Index.save_thread t } end def toggle_deleted t = cursor_thread or return multi_toggle_deleted [t] @@ -430,25 +443,33 @@ ## see comment for multi_toggle_spam def multi_toggle_deleted threads undos = threads.map { |t| actually_toggle_deleted t } UndoManager.register "deleting/undeleting #{threads.size.pluralize 'thread'}", - undos, lambda { regen_text } + undos, lambda { regen_text }, lambda { threads.each { |t| Index.save_thread t } } regen_text + threads.each { |t| Index.save_thread t } end def kill t = cursor_thread or return multi_kill [t] end + def flush_index + @flush_id = BufferManager.say "Flushing index..." + Index.save_index + BufferManager.clear @flush_id + end + ## m-m-m-m-MULTI-KILL def multi_kill threads UndoManager.register "killing #{threads.size.pluralize 'thread'}" do threads.each do |t| t.remove_label :killed add_or_unhide t.first + Index.save_thread t end regen_text end threads.each do |t| @@ -456,45 +477,24 @@ hide_thread t end regen_text BufferManager.flash "#{threads.size.pluralize 'thread'} killed." + threads.each { |t| Index.save_thread t } end - def save background=true - if background - Redwood::reporting_thread("saving thread") { actually_save } - else - actually_save - end - end - - def actually_save - @save_thread_mutex.synchronize do - BufferManager.say("Saving contacts...") { ContactManager.instance.save } - dirty_threads = @mutex.synchronize { (@threads + @hidden_threads.keys).select { |t| t.dirty? } } - next if dirty_threads.empty? - - BufferManager.say("Saving threads...") do |say_id| - dirty_threads.each_with_index do |t, i| - BufferManager.say "Saving modified thread #{i + 1} of #{dirty_threads.length}...", say_id - t.save_state Index - end - end - end - end - def cleanup UpdateManager.unregister self if @load_thread @load_thread.kill BufferManager.clear @mbid if @mbid sleep 0.1 # TODO: necessary? BufferManager.erase_flash end - save false + dirty_threads = @mutex.synchronize { (@threads + @hidden_threads.keys).select { |t| t.dirty? } } + fail "dirty threads remain" unless dirty_threads.empty? super end def toggle_tagged t = cursor_thread or return @@ -541,13 +541,15 @@ UndoManager.register "labeling thread" do thread.labels = old_labels update_text_for_line pos UpdateManager.relay self, :labeled, thread.first + Index.save_thread thread end UpdateManager.relay self, :labeled, thread.first + Index.save_thread thread end def multi_edit_labels threads user_labels = BufferManager.ask_for_labels :labels, "Add/remove labels (use -label to remove): ", [], @hidden_labels return unless user_labels @@ -577,13 +579,16 @@ UndoManager.register "labeling #{threads.size.pluralize 'thread'}" do threads.zip(old_labels).map do |t, old_labels| t.labels = old_labels UpdateManager.relay self, :labeled, t.first + Index.save_thread t end regen_text end + + threads.each { |t| Index.save_thread t } end def reply type_arg=nil t = cursor_thread or return m = t.latest_message @@ -757,34 +762,40 @@ threads = @mutex.synchronize { @threads } @text = threads.map_with_index { |t, i| text_for_thread_at i } @lines = threads.map_with_index { |t, i| [t, i] }.to_h buffer.mark_dirty if buffer end - + def authors; map { |m, *o| m.from if m }.compact.uniq; end + ## preserve author order from the thread def author_names_and_newness_for_thread t, limit=nil new = {} - authors = Set.new - t.each do |m, *o| - next unless m - break if limit and authors.size >= limit + seen = {} + authors = t.map do |m, *o| + next unless m && m.from + new[m.from] ||= m.has_label?(:unread) + next if seen[m.from] + seen[m.from] = true + m.from + end.compact - name = - if AccountManager.is_account?(m.from) - "me" - elsif t.authors.size == 1 - m.from.mediumname - else - m.from.shortname - end + result = [] + authors.each do |a| + break if limit && result.size >= limit + name = if AccountManager.is_account?(a) + "me" + elsif t.authors.size == 1 + a.mediumname + else + a.shortname + end - new[name] ||= m.has_label?(:unread) - authors << name + result << [name, new[a]] end - authors.to_a.map { |a| [a, new[a]] } + result end AUTHOR_LIMIT = 5 def text_for_thread_at line t, size_widget = @mutex.synchronize { [@threads[line], @size_widgets[line]] } @@ -841,10 +852,10 @@ size_widget_text = sprintf "%#{ @size_widget_width}s", size_widget [ [:tagged_color, @tags.tagged?(t) ? ">" : " "], - [:none, sprintf("%#{@date_width}s", date)], + [:date_color, sprintf("%#{@date_width}s", date)], (starred ? [:starred_color, "*"] : [:none, " "]), ] + from + [ [subj_color, size_widget_text],