<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array">
    <added>
      <filename>bugs/issue-871e9668f6b6ee7d26fdb7beb8dabe7ae84f0345.yaml</filename>
    </added>
    <added>
      <filename>media/profile_default.png</filename>
    </added>
  </added>
  <modified type="array">
    <modified>
      <diff>@@ -5,8 +5,8 @@ type: :feature
 component: twitterui
 release: &quot;0.2&quot;
 reporter: Arnaud Berthomier &lt;oz@cyprio.net&gt;
-status: :unstarted
-disposition: 
+status: :closed
+disposition: :fixed
 creation_time: 2008-06-01 16:14:08.582969 Z
 references: []
 
@@ -20,3 +20,7 @@ log_events:
   - Arnaud Berthomier &lt;oz@cyprio.net&gt;
   - assigned to release 0.2 from unassigned
   - &quot;&quot;
+- - 2008-06-29 20:25:40.810080 Z
+  - Arnaud Berthomier &lt;oz@cyprio.net&gt;
+  - closed issue with disposition fixed
+  - &quot;&quot;</diff>
      <filename>bugs/issue-5249e854e63967d7107f107f78aee9bc8f79dc89.yaml</filename>
    </modified>
    <modified>
      <diff>@@ -5,8 +5,8 @@ type: :bugfix
 component: twitterui
 release: &quot;0.1&quot;
 reporter: Arnaud Berthomier &lt;oz@cyprio.net&gt;
-status: :unstarted
-disposition: 
+status: :closed
+disposition: :reorg
 creation_time: 2008-06-19 07:34:43.691530 Z
 references: []
 
@@ -16,3 +16,7 @@ log_events:
   - Arnaud Berthomier &lt;oz@cyprio.net&gt;
   - created
   - &quot;&quot;
+- - 2008-06-29 20:25:25.844719 Z
+  - Arnaud Berthomier &lt;oz@cyprio.net&gt;
+  - closed issue with disposition reorg
+  - config action kills running threads. Getting back to /index will start shiny new ones.</diff>
      <filename>bugs/issue-e64196d9eaf40611e4b1059980e1f8cba7af50ba.yaml</filename>
    </modified>
    <modified>
      <diff>@@ -5,6 +5,7 @@ Shoes.setup do
   gem 'twitter'
 end
 
+require 'fileutils'
 require 'htmlentities'
 require 'twitter'
 require 'yaml'
@@ -15,6 +16,7 @@ class TwitterApp
   attr :login
   attr :password
   attr :twitter
+  attr :cache_dir
 
   def initialize
     @config_file = ENV['HOME'] + '/' + '.twitteruirc'
@@ -33,6 +35,8 @@ class TwitterApp
   end
 
   def save_config(opts)
+    opts[:cache_dir] = ENV['HOME'] + '/.twitterui/cache' unless opts[:cache_dir]
+
     File.open(@config_file, File::WRONLY|File::TRUNC|File::CREAT, 0600) do |fd|
       fd.puts YAML::dump(opts)
     end
@@ -41,10 +45,11 @@ class TwitterApp
 
   def connect(config = nil)
     unless config.nil?
-      @login    = config[:user]
-      @password = config[:password]
-      @twitter  = Twitter::Base.new @login, @password
-      @coder    = HTMLEntities.new
+      @login     = config[:user]
+      @password  = config[:password]
+      @cache_dir = config[:cache_dir]
+      @twitter   = Twitter::Base.new @login, @password
+      @coder     = HTMLEntities.new
     end
   end
 
@@ -115,7 +120,13 @@ class TwitterUI &lt; Shoes
 
   # &quot;Main&quot; config page.
   def config
-    @@context[:busy] = true
+    # Kill twitter + watcher thread
+    %w{twitter_thread check_thread}.each do |t|
+      @@context[t.to_sym] = kill_thread @@context[t.to_sym]
+    end
+    @@context[:tweets_flow] = nil
+    @status_msg = nil
+
     background gray(0.1)
     login = @@context[:twitter].login ? @@context[:twitter].login : 'username?'
     stack :width =&gt; 1.0 do
@@ -147,31 +158,23 @@ class TwitterUI &lt; Shoes
   def index
     background black
     display_control_box
-    @@context[:busy] = nil
-    @@context[:tweets_flow] = nil
+    @@context[:twitter] = TwitterApp.new 
+    visit '/first_config' if @@context[:twitter].login.nil?
+    load_tweets 'Loading...'
+    wait_for_tweets
 
-    if @@context[:twitter].nil?
-      @@context[:twitter] = TwitterApp.new 
-      visit '/first_config' if @@context[:twitter].login.nil?
-      load_tweets 'Loading...'
-      wait_for_tweets
-
-      # Check (in a rather clumsy way) that we're not waiting
-      # Twitter too long.
-      Thread.new do
-        while true do
-          timeouted = (Time.now - @@context[:twitter_check]) &gt; @@context[:timeout]
-          if @@context[:twitter_check] != nil &amp;&amp; timeouted &amp;&amp; !@@context[:busy]
-            @@context[:twitter_thread].terminate
-            @@context[:twitter_thread] = nil
-            load_tweets 'Loading...'
-            wait_for_tweets
-          end
-          sleep 5
+    # Check that we're not waiting for Twitter too long.
+    Thread.new do
+      @@context[:check_thread] = Thread.current
+      while true do
+        timeouted = (Time.now - @@context[:twitter_check]) &gt; @@context[:timeout]
+        if @@context[:twitter_check] != nil &amp;&amp; timeouted
+          @@context[:twitter_thread] = kill_thread @@context[:twitter_thread]
+          load_tweets 'Loading...'
+          wait_for_tweets
         end
+        sleep 5
       end
-    else
-      load_tweets 'Loading...'
     end
 
     # Keyboard shortcuts.
@@ -288,10 +291,9 @@ class TwitterUI &lt; Shoes
         stack :width =&gt; -20, :margin =&gt; 5 do
           bg_color = ( status.user.screen_name != @@context[:twitter].login ) ? &quot;#191919&quot; : &quot;#39414A&quot;
           background bg_color, :curve =&gt; 10
-
           flow :width =&gt; -5 do
             stack :width =&gt; 48, :margin =&gt; 5 do
-              image status.user.profile_image_url   # FIXME cache please
+              image cached_image(status.user)
             end
             stack :width =&gt; -48, :margin =&gt; 10 do
               eval &quot;para &quot; + format_status(status, bg_color) +
@@ -318,21 +320,16 @@ class TwitterUI &lt; Shoes
       @@context[:twitter_thread] = Thread.current
       while true do
 
-        # Don't load anything if the app is busy in config mode or whatever.
-        # (I wish shoes supported multiple windows ^^;)
-        unless @@context[:busy]
-          # Ask Twitter.
-          @@context[:twitter_check] = Time.now
-          tweets = @@context[:twitter].tweets
-          @@context[:twitter_check] = nil
-        end
+        # Ask Twitter.
+        @@context[:twitter_check] = Time.now
+        tweets = @@context[:twitter].tweets
+        @@context[:twitter_check] = nil
 
         # Display timeline if needed, then wait until it's time to reload
         # again...
         @@context[:seconds_to_reload] = @@context[:sleeptime]
         if last_tweets.nil? || 
-           tweets.zip(last_tweets).any? {|t,l|t.created_at!=l.created_at} ||
-           @@context[:tweets_flow] == nil
+           tweets.zip(last_tweets).any? {|t,l|t.created_at!=l.created_at}
           display_tweets(tweets)
         end
         last_tweets = tweets
@@ -391,6 +388,35 @@ class TwitterUI &lt; Shoes
         &quot;#{(dt/60/60/60).to_i} days ago&quot;
     end
   end
+
+  # Thread terminator
+  def kill_thread(thread)
+    thread.terminate
+    nil
+  end
+
+  # Cache profile images
+  def cached_image(user)
+    cache_dir = @@context[:twitter].cache_dir
+    FileUtils.mkdir_p(cache_dir) unless File.exists? cache_dir
+
+    # Cache file with friend's name, keeping the extension
+    # to not confuse Shoes about the file type...
+    cache = cache_dir + '/' + user.screen_name + '.' + user.profile_image_url.gsub(/.*\./, '')
+    unless File.exists? cache
+      begin
+        open(user.profile_image_url) do |fd|
+          open(cache, 'w') do |cache_fd|
+            cache_fd.write fd.read
+          end
+        end
+      rescue =&gt; exc
+        puts &quot;Error: can't fetch profile image: #{exc}&quot;
+        cache = &quot;media/profile_default.png&quot;
+      end
+    end
+    cache
+  end
 end
 
 Shoes.app :title =&gt; 'Twitter UI', :width =&gt; 300, :height =&gt; 350</diff>
      <filename>twitterui.rb</filename>
    </modified>
  </modified>
  <removed type="array"/>
  <parents type="array">
    <parent>
      <id>57482a95cb5369fc6ae7251a6a2b0ba307920ae4</id>
    </parent>
  </parents>
  <author>
    <name>oz</name>
    <email>oz@lab.cyprio.net</email>
  </author>
  <url>http://github.com/oz/twitterui/commit/42b0b663bccbcf18e3876efe59cfd9e4f0f733ef</url>
  <id>42b0b663bccbcf18e3876efe59cfd9e4f0f733ef</id>
  <committed-date>2008-06-29T13:33:36-07:00</committed-date>
  <authored-date>2008-06-29T13:33:36-07:00</authored-date>
  <message>Fixed config/index cycle, and added cache to friends' images to get snappier reloads</message>
  <tree>f90c3bd9fa3ad721fee03c585ebd06d82c2cc156</tree>
  <committer>
    <name>oz</name>
    <email>oz@lab.cyprio.net</email>
  </committer>
</commit>
