twterm 1.0.8 → 1.0.9

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 9f8922ec344713188266282109b5d5a43a95948e
4
- data.tar.gz: b1ac62bbd18b92a058b8f0753a1f34470b129fd0
3
+ metadata.gz: 6b7f3f957fa6bb231072502fd2794d212db4547e
4
+ data.tar.gz: 83aecc6b84ed61620f52b69f6b65c91b6d1af67b
5
5
  SHA512:
6
- metadata.gz: c08ff7689bd89c875051779cd9eda592c699df11a855a56dbe83fb4ac1a67728de75477ff8152332b36b632456169f7490759c81555542fe46dc546d0125392d
7
- data.tar.gz: 6f920de275acdac03b647505fe37debe4defedd67f40dfb5c3eb9b0281a4382f688bd6266e4378c86411133b9ff4bd5197badf26c163fbb1f11775d2cfd86293
6
+ metadata.gz: e3b1748a40af7f4a32b93568422e0cd65dbe6a9e7a12818567e8aff5142639af6396df93febdffd0f93a19afd9020bf4ac5f86ad468f60b00c5e8cdb597ee5ee
7
+ data.tar.gz: 6df0822a5f0d59a9f1e2e260e2e43a2b5997a7a36426f5dc786f85283514c9d44ddd9ca74dc41fd7ff1fbc83085bf2eb84a6b4af8b12f33b3438a91ec7d3438c
@@ -34,6 +34,7 @@ require 'twterm/scheduler'
34
34
  require 'twterm/status'
35
35
  require 'twterm/tab_manager'
36
36
  require 'twterm/tab/base'
37
+ require 'twterm/tab/dumpable'
37
38
  require 'twterm/tab/exceptions'
38
39
  require 'twterm/tab/scrollable'
39
40
  require 'twterm/tab/statuses_tab'
@@ -54,6 +55,6 @@ require 'twterm/version'
54
55
 
55
56
  module Twterm
56
57
  class Conf
57
- REQUIRE_VERSION = '1.0.8'
58
+ REQUIRE_VERSION = '1.0.9'
58
59
  end
59
60
  end
@@ -19,6 +19,7 @@ module Twterm
19
19
 
20
20
  mentions_tab = Tab::MentionsTab.new(client)
21
21
  TabManager.instance.add(mentions_tab)
22
+ TabManager.instance.recover_tabs
22
23
 
23
24
  Screen.instance.refresh
24
25
 
@@ -37,15 +38,17 @@ module Twterm
37
38
 
38
39
  def register_interruption_handler(&block)
39
40
  fail ArgumentError, 'no block given' unless block_given?
40
- Signal.trap(:INT) do
41
- block.call
42
- end
41
+ Signal.trap(:INT) { block.call }
43
42
  end
44
43
 
45
44
  def reset_interruption_handler
46
- Signal.trap(:INT) do
47
- exit
48
- end
45
+ Signal.trap(:INT) { App.instance.quit }
46
+ end
47
+
48
+ def quit
49
+ Curses.close_screen
50
+ TabManager.instance.dump_tabs
51
+ exit
49
52
  end
50
53
 
51
54
  private
@@ -43,9 +43,7 @@ module Twterm
43
43
 
44
44
  def stream
45
45
  @stream_client.on_friends do
46
- break if @stream_connected
47
-
48
- Notifier.instance.show_message 'Connection established'
46
+ Notifier.instance.show_message 'Connection established' unless @stream_connected
49
47
  @stream_connected = true
50
48
  end
51
49
 
@@ -117,13 +115,19 @@ module Twterm
117
115
  end
118
116
  end
119
117
 
118
+ def list(list_id)
119
+ send_request do
120
+ yield List.new(@rest_client.list(list_id))
121
+ end
122
+ end
123
+
120
124
  def lists
121
125
  send_request do
122
126
  yield @rest_client.lists.map { |list| List.new(list) }
123
127
  end
124
128
  end
125
129
 
126
- def list(list)
130
+ def list_timeline(list)
127
131
  fail ArgumentError, 'argument must be an instance of List class' unless list.is_a? List
128
132
  send_request do
129
133
  yield @rest_client.list_timeline(list.id, count: 100).select(&@mute_filter).map(&CREATE_STATUS_PROC)
@@ -144,11 +148,12 @@ module Twterm
144
148
 
145
149
  def show_user(query)
146
150
  send_request do
147
- begin
148
- user = User.new(@rest_client.user(query))
149
- rescue Twitter::Error::NotFound
150
- user = nil
151
- end
151
+ user =
152
+ begin
153
+ User.new(@rest_client.user(query))
154
+ rescue Twitter::Error::NotFound
155
+ nil
156
+ end
152
157
  yield user
153
158
  end
154
159
  end
@@ -249,9 +254,11 @@ module Twterm
249
254
  begin
250
255
  block.call
251
256
  rescue Twitter::Error => e
252
- Notifier.instance.show_error 'Failed to send request'
253
- sleep 10
254
- retry if e.message == 'getaddrinfo: nodename nor servname provided, or not known'
257
+ Notifier.instance.show_error "Failed to send request: #{e.message}"
258
+ if e.message == 'getaddrinfo: nodename nor servname provided, or not known'
259
+ sleep 10
260
+ retry
261
+ end
255
262
  end
256
263
  end
257
264
  end
@@ -2,8 +2,17 @@ module Twterm
2
2
  class List
3
3
  attr_reader :id, :name, :slug, :full_name, :mode, :description, :member_count, :subscriber_count
4
4
 
5
+ @@instances = {}
6
+
5
7
  def initialize(list)
6
8
  @id = list.id
9
+ update!(list)
10
+
11
+ @@instances[@id] = self
12
+ self
13
+ end
14
+
15
+ def update!(list)
7
16
  @name = list.name
8
17
  @slug = list.slug
9
18
  @full_name = list.full_name
@@ -11,10 +20,32 @@ module Twterm
11
20
  @description = list.description.is_a?(Twitter::NullObject) ? '' : list.description
12
21
  @member_count = list.member_count
13
22
  @subscriber_count = list.subscriber_count
23
+
24
+ self
14
25
  end
15
26
 
16
27
  def ==(other)
17
28
  other.is_a?(self.class) && id == other.id
18
29
  end
30
+
31
+ def self.new(list)
32
+ instance = find(list.id)
33
+ instance.nil? ? super : instance.update!(list)
34
+ end
35
+
36
+ def self.find(id)
37
+ @@instances[id]
38
+ end
39
+
40
+ def self.find_or_fetch(id)
41
+ instance = find(id)
42
+ (yield(instance) && return) if instance
43
+
44
+ Client.current.list(id) { |list| yield list }
45
+ end
46
+
47
+ def self.all
48
+ @@instances.values
49
+ end
19
50
  end
20
51
  end
@@ -44,7 +44,7 @@ module Twterm
44
44
  Tweetbox.instance.compose
45
45
  return
46
46
  when 'Q'
47
- exit
47
+ App.instance.quit
48
48
  when '/'
49
49
  # filter
50
50
  else
@@ -89,14 +89,24 @@ module Twterm
89
89
  end
90
90
 
91
91
  def in_reply_to_status(&block)
92
- block.call(nil) if @in_reply_to_status_id.nil?
92
+ if @in_reply_to_status_id.nil?
93
+ block.call(nil)
94
+ return
95
+ end
93
96
 
94
97
  status = Status.find(@in_reply_to_status_id)
95
- block.call(status) unless status.nil?
98
+ unless status.nil?
99
+ block.call(status)
100
+ return
101
+ end
96
102
 
97
103
  Client.current.show_status(@in_reply_to_status_id, &block)
98
104
  end
99
105
 
106
+ def replies
107
+ Status.all.select { |s| s.in_reply_to_status_id == id }
108
+ end
109
+
100
110
  def retweeted_by
101
111
  User.find(@retweeted_by_user_id)
102
112
  end
@@ -122,6 +132,13 @@ module Twterm
122
132
  @@instances[id]
123
133
  end
124
134
 
135
+ def find_or_fetch(id)
136
+ instance = find(id)
137
+ (yield(instance) && return) if instance
138
+
139
+ Client.current.show_status(id) { |status| yield status }
140
+ end
141
+
125
142
  def parse_time(time)
126
143
  (time.is_a?(String) ? Time.parse(time) : time.dup).localtime
127
144
  end
@@ -3,7 +3,7 @@ module Twterm
3
3
  module Base
4
4
  include Curses
5
5
 
6
- attr_reader :title
6
+ attr_accessor :title
7
7
 
8
8
  def initialize
9
9
  @window = stdscr.subwin(stdscr.maxy - 5, stdscr.maxx - 30, 3, 0)
@@ -2,30 +2,48 @@ module Twterm
2
2
  module Tab
3
3
  class ConversationTab
4
4
  include StatusesTab
5
+ include Dumpable
5
6
 
6
7
  attr_reader :status
7
8
 
8
- def initialize(status)
9
- fail ArgumentError, 'argument must be an instance of Status class' unless status.is_a? Status
10
-
9
+ def initialize(status_id)
11
10
  @title = 'Conversation'
12
-
13
11
  super()
14
- prepend(status)
15
- Thread.new { fetch_reply(status) }
12
+
13
+ Status.find_or_fetch(status_id) do |status|
14
+ @status = status
15
+
16
+ append(status)
17
+ move_to_top
18
+ Thread.new { fetch_in_reply_to_status(status) }
19
+ Thread.new { fetch_replies(status) }
20
+ end
16
21
  end
17
22
 
18
- def fetch_reply(status)
19
- status.in_reply_to_status do |reply|
20
- return if reply.nil?
21
- append(reply)
22
- fetch_reply(reply)
23
+ def fetch_in_reply_to_status(status)
24
+ status.in_reply_to_status do |in_reply_to|
25
+ return if in_reply_to.nil?
26
+ append(in_reply_to)
27
+ sort
28
+ Thread.new { fetch_in_reply_to_status(in_reply_to) }
29
+ end
30
+ end
31
+
32
+ def fetch_replies(status)
33
+ status.replies.each do |reply|
34
+ prepend(reply)
35
+ sort
36
+ Thread.new { fetch_replies(reply) }
23
37
  end
24
38
  end
25
39
 
26
40
  def ==(other)
27
41
  other.is_a?(self.class) && status == other.status
28
42
  end
43
+
44
+ def dump
45
+ @status.id
46
+ end
29
47
  end
30
48
  end
31
49
  end
@@ -0,0 +1,21 @@
1
+ module Twterm
2
+ module Tab
3
+ module Dumpable
4
+ def dump
5
+ fail NotImplementedError 'dump method must be implemented'
6
+ end
7
+
8
+ def self.included(klass)
9
+ klass.extend(ClassMethods)
10
+ end
11
+
12
+ module ClassMethods
13
+ def recover(title, arg)
14
+ tab = new(arg)
15
+ tab.title = title
16
+ tab
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -2,23 +2,25 @@ module Twterm
2
2
  module Tab
3
3
  class ListTab
4
4
  include StatusesTab
5
+ include Dumpable
5
6
 
6
7
  attr_reader :list
7
8
 
8
- def initialize(list)
9
- fail ArgumentError, 'argument must be an instance of List class' unless list.is_a? List
10
-
9
+ def initialize(list_id)
11
10
  super()
12
11
 
13
- @list = list
14
- @title = @list.full_name
15
- fetch { move_to_top }
16
- @auto_reloader = Scheduler.new(300) { fetch }
12
+ List.find_or_fetch(list_id) do |list|
13
+ @list = list
14
+ @title = @list.full_name
15
+ TabManager.instance.refresh_window
16
+ fetch { move_to_top }
17
+ @auto_reloader = Scheduler.new(300) { fetch }
18
+ end
17
19
  end
18
20
 
19
21
  def fetch
20
22
  client = Client.current
21
- client.list(@list) do |statuses|
23
+ client.list_timeline(@list) do |statuses|
22
24
  statuses.reverse.each(&method(:prepend))
23
25
  sort
24
26
  yield if block_given?
@@ -26,13 +28,17 @@ module Twterm
26
28
  end
27
29
 
28
30
  def close
29
- @auto_reloader.kill
31
+ @auto_reloader.kill if @auto_reloader
30
32
  super
31
33
  end
32
34
 
33
35
  def ==(other)
34
36
  other.is_a?(self.class) && list == other.list
35
37
  end
38
+
39
+ def dump
40
+ @list.id
41
+ end
36
42
  end
37
43
  end
38
44
  end
@@ -19,7 +19,7 @@ module Twterm
19
19
  case key
20
20
  when 10
21
21
  return true if current_list.nil?
22
- list_tab = Tab::ListTab.new(current_list)
22
+ list_tab = Tab::ListTab.new(current_list.id)
23
23
  TabManager.instance.switch(list_tab)
24
24
  else
25
25
  return false
@@ -25,8 +25,9 @@ module Twterm
25
25
 
26
26
  input_thread = Thread.new do
27
27
  close_screen
28
+ CompletionManager.instance.set_default_mode!
28
29
  puts "\ninput search query"
29
- query = (readline('input query > ') || '').strip
30
+ query = (readline('> ') || '').strip
30
31
  resetter.call
31
32
 
32
33
  tab = query.nil? || query.empty? ? Tab::New::Start.new : Tab::SearchTab.new(query)
@@ -31,15 +31,18 @@ module Twterm
31
31
  screen_name = (readline('> @') || '').strip
32
32
  resetter.call
33
33
 
34
- Client.current.show_user(screen_name) do |user|
35
- if screen_name.nil? || screen_name.empty? || user.nil?
36
- Notifier.instance.show_error 'User not found' if user.nil?
37
- tab = Tab::New::Start.new
38
- else
39
- tab = Tab::UserTab.new(user)
34
+ if screen_name.nil? || screen_name.empty?
35
+ TabManager.instance.switch(Tab::New::Start.new)
36
+ else
37
+ Client.current.show_user(screen_name) do |user|
38
+ if user.nil?
39
+ Notifier.instance.show_error 'User not found'
40
+ tab = Tab::New::Start.new
41
+ else
42
+ tab = Tab::UserTab.new(user.id)
43
+ end
44
+ TabManager.instance.switch(tab)
40
45
  end
41
-
42
- TabManager.instance.switch(tab)
43
46
  end
44
47
  end
45
48
 
@@ -108,7 +108,7 @@ module Twterm
108
108
  0
109
109
  else
110
110
  height = @window.maxy
111
- [height * (last - index + 1) / count, 1].max
111
+ [height * (last - offset + 1) / count, 1].max
112
112
  end
113
113
  end
114
114
 
@@ -2,6 +2,7 @@ module Twterm
2
2
  module Tab
3
3
  class SearchTab
4
4
  include StatusesTab
5
+ include Dumpable
5
6
 
6
7
  attr_reader :query
7
8
 
@@ -12,6 +13,7 @@ module Twterm
12
13
  @title = "\"#{@query}\""
13
14
 
14
15
  fetch { move_to_top }
16
+ @auto_reloader = Scheduler.new(300) { fetch }
15
17
  end
16
18
 
17
19
  def fetch
@@ -21,9 +23,18 @@ module Twterm
21
23
  end
22
24
  end
23
25
 
26
+ def close
27
+ @auto_reloader.kill if @auto_reloader
28
+ super
29
+ end
30
+
24
31
  def ==(other)
25
32
  other.is_a?(self.class) && query == other.query
26
33
  end
34
+
35
+ def dump
36
+ @query
37
+ end
27
38
  end
28
39
  end
29
40
  end
@@ -82,7 +82,7 @@ module Twterm
82
82
  def show_user
83
83
  return if highlighted_status.nil?
84
84
  user = highlighted_status.user
85
- user_tab = Tab::UserTab.new(user)
85
+ user_tab = Tab::UserTab.new(user.id)
86
86
  TabManager.instance.add_and_show(user_tab)
87
87
  end
88
88
 
@@ -95,7 +95,7 @@ module Twterm
95
95
 
96
96
  def show_conversation
97
97
  return if highlighted_status.nil?
98
- tab = Tab::ConversationTab.new(highlighted_status)
98
+ tab = Tab::ConversationTab.new(highlighted_status.id)
99
99
  TabManager.instance.add_and_show(tab)
100
100
  end
101
101
 
@@ -112,6 +112,8 @@ module Twterm
112
112
 
113
113
  @window.clear
114
114
 
115
+ return if offset < 0
116
+
115
117
  statuses.reverse.drop(offset).each.with_index(offset) do |status, i|
116
118
  formatted_lines = status.split(@window.maxx - 4).count
117
119
  if current_line + formatted_lines + 2 > @window.maxy
@@ -2,19 +2,21 @@ module Twterm
2
2
  module Tab
3
3
  class UserTab
4
4
  include StatusesTab
5
+ include Dumpable
5
6
 
6
7
  attr_reader :user
7
8
 
8
- def initialize(user)
9
- fail ArgumentError, 'argument must be an instance of User class' unless user.is_a? User
10
-
9
+ def initialize(user_id)
11
10
  super()
12
11
 
13
- @user = user
14
- @title = "@#{user.screen_name}"
12
+ User.find_or_fetch(user_id) do |user|
13
+ @user = user
14
+ @title = "@#{@user.screen_name}"
15
+ TabManager.instance.refresh_window
15
16
 
16
- fetch { move_to_top }
17
- @auto_reloader = Scheduler.new(120) { fetch }
17
+ fetch { move_to_top }
18
+ @auto_reloader = Scheduler.new(120) { fetch }
19
+ end
18
20
  end
19
21
 
20
22
  def fetch
@@ -26,13 +28,17 @@ module Twterm
26
28
  end
27
29
 
28
30
  def close
29
- @auto_reloader.kill
31
+ @auto_reloader.kill if @auto_reloader
30
32
  super
31
33
  end
32
34
 
33
35
  def ==(other)
34
36
  other.is_a?(self.class) && user == other.user
35
37
  end
38
+
39
+ def dump
40
+ @user.id
41
+ end
36
42
  end
37
43
  end
38
44
  end
@@ -3,6 +3,8 @@ module Twterm
3
3
  include Singleton
4
4
  include Curses
5
5
 
6
+ DUMPED_TABS_FILE = "#{ENV['HOME']}/.twterm/dumped_tabs"
7
+
6
8
  def initialize
7
9
  @tabs = []
8
10
  @index = 0
@@ -78,6 +80,28 @@ module Twterm
78
80
  end
79
81
  end
80
82
 
83
+ def recover_tabs
84
+ return unless File.exist? DUMPED_TABS_FILE
85
+
86
+ data = YAML.load(File.read(DUMPED_TABS_FILE))
87
+ data.each do |klass, title, arg|
88
+ tab = klass.recover(title, arg)
89
+ add(tab)
90
+ end
91
+ rescue
92
+ Notifier.instance.show_error 'Failed to recover tabs'
93
+ end
94
+
95
+ def dump_tabs
96
+ data = @tabs.each_with_object([]) do |tab, arr|
97
+ next unless tab.is_a? Tab::Dumpable
98
+ arr << [tab.class, tab.title, tab.dump]
99
+ end
100
+ File.open(DUMPED_TABS_FILE, 'w', 0600) do |f|
101
+ f.write data.to_yaml
102
+ end
103
+ end
104
+
81
105
  def refresh_window
82
106
  @window.clear
83
107
  current_tab_id = current_tab.object_id
@@ -57,6 +57,13 @@ module Twterm
57
57
  @@instances[id]
58
58
  end
59
59
 
60
+ def self.find_or_fetch(id)
61
+ instance = find(id)
62
+ (yield(instance) && return) if instance
63
+
64
+ Client.current.show_user(id) { |user| yield user }
65
+ end
66
+
60
67
  def self.cleanup
61
68
  referenced_users = Status.all.map(&:user)
62
69
  referenced_users.each(&:touch!)
@@ -1,3 +1,3 @@
1
1
  module Twterm
2
- VERSION = '1.0.8'
2
+ VERSION = '1.0.9'
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: twterm
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.8
4
+ version: 1.0.9
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ryota Kameoka
@@ -159,6 +159,7 @@ files:
159
159
  - lib/twterm/status.rb
160
160
  - lib/twterm/tab/base.rb
161
161
  - lib/twterm/tab/conversation_tab.rb
162
+ - lib/twterm/tab/dumpable.rb
162
163
  - lib/twterm/tab/exceptions.rb
163
164
  - lib/twterm/tab/favorites.rb
164
165
  - lib/twterm/tab/list_tab.rb