twterm 1.1.3 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. checksums.yaml +4 -4
  2. data/bin/twterm +4 -4
  3. data/lib/twterm/app.rb +19 -4
  4. data/lib/twterm/client.rb +8 -470
  5. data/lib/twterm/direct_message.rb +82 -0
  6. data/lib/twterm/direct_message_composer.rb +74 -0
  7. data/lib/twterm/direct_message_manager.rb +52 -0
  8. data/lib/twterm/event/base.rb +22 -0
  9. data/lib/twterm/event/direct_message/fetched.rb +10 -0
  10. data/lib/twterm/event/favorite.rb +18 -0
  11. data/lib/twterm/event/follow.rb +17 -0
  12. data/lib/twterm/event/notification.rb +33 -0
  13. data/lib/twterm/event/open_uri.rb +11 -0
  14. data/lib/twterm/event/screen/resize.rb +13 -0
  15. data/lib/twterm/event/status/base.rb +14 -0
  16. data/lib/twterm/event/status/delete.rb +13 -0
  17. data/lib/twterm/event/status/mention.rb +10 -0
  18. data/lib/twterm/event/status/timeline.rb +10 -0
  19. data/lib/twterm/event_dispatcher.rb +59 -0
  20. data/lib/twterm/filter_query_window.rb +11 -5
  21. data/lib/twterm/filterable_list.rb +6 -1
  22. data/lib/twterm/notifier.rb +39 -15
  23. data/lib/twterm/promise.rb +2 -2
  24. data/lib/twterm/publisher.rb +16 -0
  25. data/lib/twterm/rest_client.rb +401 -0
  26. data/lib/twterm/screen.rb +16 -13
  27. data/lib/twterm/status.rb +12 -1
  28. data/lib/twterm/streaming_client.rb +103 -0
  29. data/lib/twterm/subscriber.rb +33 -0
  30. data/lib/twterm/tab/base.rb +13 -6
  31. data/lib/twterm/tab/direct_message/conversation.rb +103 -0
  32. data/lib/twterm/tab/direct_message/conversation_list.rb +99 -0
  33. data/lib/twterm/tab/key_assignments_cheatsheet.rb +3 -2
  34. data/lib/twterm/tab/new/list.rb +5 -3
  35. data/lib/twterm/tab/new/search.rb +3 -2
  36. data/lib/twterm/tab/new/start.rb +17 -2
  37. data/lib/twterm/tab/new/user.rb +6 -3
  38. data/lib/twterm/tab/statuses/base.rb +18 -11
  39. data/lib/twterm/tab/statuses/conversation.rb +3 -2
  40. data/lib/twterm/tab/statuses/favorites.rb +3 -2
  41. data/lib/twterm/tab/statuses/home.rb +10 -4
  42. data/lib/twterm/tab/statuses/list_timeline.rb +3 -2
  43. data/lib/twterm/tab/statuses/mentions.rb +6 -6
  44. data/lib/twterm/tab/statuses/search.rb +4 -3
  45. data/lib/twterm/tab/statuses/user_timeline.rb +3 -2
  46. data/lib/twterm/tab/user_tab.rb +26 -16
  47. data/lib/twterm/tab/users/base.rb +3 -2
  48. data/lib/twterm/tab/users/followers.rb +3 -2
  49. data/lib/twterm/tab/users/friends.rb +3 -2
  50. data/lib/twterm/tab_manager.rb +20 -8
  51. data/lib/twterm/tweetbox.rb +5 -2
  52. data/lib/twterm/uri_opener.rb +25 -0
  53. data/lib/twterm/user.rb +11 -1
  54. data/lib/twterm/utils.rb +13 -0
  55. data/lib/twterm/version.rb +1 -1
  56. data/lib/twterm.rb +0 -3
  57. data/spec/twterm/event/screen/resize_spec.rb +11 -0
  58. data/spec/twterm/event_dispatcher_spec.rb +19 -0
  59. data/twterm.gemspec +1 -1
  60. metadata +29 -7
  61. data/lib/twterm/notification/base.rb +0 -24
  62. data/lib/twterm/notification/error.rb +0 -19
  63. data/lib/twterm/notification/message.rb +0 -19
@@ -0,0 +1,401 @@
1
+ require 'twterm/direct_message'
2
+ require 'twterm/direct_message_manager'
3
+ require 'twterm/publisher'
4
+ require 'twterm/event/notification'
5
+
6
+ module Twterm
7
+ module RESTClient
8
+ include Publisher
9
+
10
+ CONSUMER_KEY = 'vLNSVFgXclBJQJRZ7VLMxL9lA'.freeze
11
+ CONSUMER_SECRET = 'OFLKzrepRG2p1hq0nUB9j2S9ndFQoNTPheTpmOY0GYw55jGgS5'.freeze
12
+
13
+ def block(*user_ids)
14
+ send_request do
15
+ rest_client.block(*user_ids)
16
+ end.then do |users|
17
+ users.each do |user|
18
+ Friendship.block(self.user_id, user.id)
19
+ end
20
+ end
21
+ end
22
+
23
+ def create_direct_message(recipient, text)
24
+ send_request do
25
+ rest_client.create_direct_message(recipient.id, text)
26
+ end.then do |message|
27
+ msg = DirectMessage.new(message)
28
+ direct_message_manager.add(msg.recipient, msg)
29
+ publish(Event::DirectMessage::Fetched.new)
30
+ publish(Event::Notification.new(:message, 'Your message to @%s has been sent' % msg.recipient.screen_name))
31
+ end
32
+ end
33
+
34
+ def direct_message_conversations
35
+ direct_message_manager.conversations
36
+ end
37
+
38
+ def direct_messages_received
39
+ send_request do
40
+ rest_client.direct_messages(count: 200).map(&DirectMessage.method(:new))
41
+ end
42
+ end
43
+
44
+ def direct_messages_sent
45
+ send_request do
46
+ rest_client.direct_messages_sent(count: 200).map(&DirectMessage.method(:new))
47
+ end
48
+ end
49
+
50
+ def destroy_status(status)
51
+ send_request_without_catch do
52
+ rest_client.destroy_status(status.id)
53
+ publish(Event::Status::Delete.new(status.id))
54
+ publish(Event::Notification.new(:message, 'Your tweet has been deleted'))
55
+ end.catch do |reason|
56
+ case reason
57
+ when Twitter::Error::NotFound, Twitter::Error::Forbidden
58
+ publish(Event::Notification.new(:error, 'You cannot destroy that status'))
59
+ else
60
+ raise reason
61
+ end
62
+ end.catch(&show_error)
63
+ end
64
+
65
+ def favorite(status)
66
+ return false unless status.is_a? Status
67
+
68
+ send_request do
69
+ rest_client.favorite(status.id)
70
+ end.then do
71
+ status.favorite!
72
+ publish(Event::Notification.new(:message, 'Successfully liked: @%s "%s"' % [
73
+ status.user.screen_name, status.text
74
+ ]))
75
+ end
76
+ end
77
+
78
+ def favorites(user_id = nil)
79
+ user_id ||= self.user_id
80
+
81
+ send_request do
82
+ rest_client.favorites(user_id, count: 200)
83
+ end.then do |tweets|
84
+ tweets.map(&Status.method(:new))
85
+ end
86
+ end
87
+
88
+ def fetch_muted_users
89
+ send_request do
90
+ @muted_user_ids = rest_client.muted_ids.to_a
91
+ end
92
+ end
93
+
94
+ def follow(*user_ids)
95
+ send_request do
96
+ rest_client.follow(*user_ids)
97
+ end.then do |users|
98
+ users.each do |user|
99
+ if user.protected?
100
+ Friendship.following_requested(self.user_id, user.id)
101
+ else
102
+ Friendship.follow(self.user_id, user.id)
103
+ end
104
+ end
105
+ end
106
+ end
107
+
108
+ def followers(user_id = nil)
109
+ user_id ||= self.user_id
110
+
111
+ m = Mutex.new
112
+
113
+ send_request do
114
+ rest_client.follower_ids(user_id).each_slice(100) do |user_ids|
115
+ m.synchronize do
116
+ users = rest_client.users(*user_ids).map(& -> u { User.new(u) })
117
+ users.each do |user|
118
+ Friendship.follow(user.id, self.user_id)
119
+ end if user_id == self.user_id
120
+ yield users
121
+ end
122
+ end
123
+ end
124
+ end
125
+
126
+ def friends(user_id = nil)
127
+ user_id ||= self.user_id
128
+
129
+ m = Mutex.new
130
+
131
+ send_request do
132
+ rest_client.friend_ids(user_id).each_slice(100) do |user_ids|
133
+ m.synchronize do
134
+ yield rest_client.users(*user_ids).map(& -> u { User.new(u) })
135
+ end
136
+ end
137
+ end
138
+ end
139
+
140
+ def home_timeline
141
+ send_request do
142
+ rest_client.home_timeline(count: 200)
143
+ end.then do |statuses|
144
+ statuses
145
+ .select(&@mute_filter)
146
+ .map(&Status.method(:new))
147
+ end
148
+ end
149
+
150
+ def list(list_id)
151
+ send_request do
152
+ rest_client.list(list_id)
153
+ end.then do |list|
154
+ List.new(list)
155
+ end
156
+ end
157
+
158
+ def list_timeline(list)
159
+ fail ArgumentError,
160
+ 'argument must be an instance of List class' unless list.is_a? List
161
+ send_request do
162
+ rest_client.list_timeline(list.id, count: 200)
163
+ end.then do |statuses|
164
+ statuses
165
+ .select(&@mute_filter)
166
+ .map(&Status.method(:new))
167
+ end
168
+ end
169
+
170
+ def lists
171
+ send_request do
172
+ rest_client.lists
173
+ end.then do |lists|
174
+ lists.map { |list| List.new(list) }
175
+ end
176
+ end
177
+
178
+ def lookup_friendships
179
+ user_ids = User.ids.reject { |id| Friendship.already_looked_up?(id) }
180
+ send_request_without_catch do
181
+ user_ids.each_slice(100) do |chunked_user_ids|
182
+ friendships = rest_client.friendships(*chunked_user_ids)
183
+ friendships.each do |friendship|
184
+ id = friendship.id
185
+ client_id = user_id
186
+
187
+ conn = friendship.connections
188
+ conn.include?('blocking') ? Friendship.block(client_id, id) : Friendship.unblock(client_id, id)
189
+ conn.include?('following') ? Friendship.follow(client_id, id) : Friendship.unfollow(client_id, id)
190
+ conn.include?('following_requested') ? Friendship.following_requested(client_id, id) : Friendship.following_not_requested(client_id, id)
191
+ conn.include?('followed_by') ? Friendship.follow(id, client_id) : Friendship.unfollow(id, client_id)
192
+ conn.include?('muting') ? Friendship.mute(client_id, id) : Friendship.unmute(client_id, id)
193
+ end
194
+ end
195
+ end.catch do |e|
196
+ case e
197
+ when Twitter::Error::TooManyRequests
198
+ # do nothing
199
+ else
200
+ raise e
201
+ end
202
+ end.catch(&show_error)
203
+ end
204
+
205
+ def mentions
206
+ send_request do
207
+ rest_client.mentions(count: 200)
208
+ end.then do |statuses|
209
+ statuses
210
+ .select(&@mute_filter)
211
+ .map(&Status.method(:new))
212
+ end
213
+ end
214
+
215
+ def mute(user_ids)
216
+ send_request do
217
+ rest_client.mute(*user_ids)
218
+ end.then do |users|
219
+ users.each do |user|
220
+ Friendship.mute(self.user_id, user.id)
221
+ end
222
+ end
223
+ end
224
+
225
+ def post(text, in_reply_to = nil)
226
+ send_request do
227
+ if in_reply_to.is_a? Status
228
+ text = "@#{in_reply_to.user.screen_name} #{text}"
229
+ rest_client.update(text, in_reply_to_status_id: in_reply_to.id)
230
+ else
231
+ rest_client.update(text)
232
+ end
233
+ publish(Event::Notification.new(:message, 'Your tweet has been posted'))
234
+ end
235
+ end
236
+
237
+ def retweet(status)
238
+ fail ArgumentError,
239
+ 'argument must be an instance of Status class' unless status.is_a? Status
240
+
241
+ send_request_without_catch do
242
+ rest_client.retweet!(status.id)
243
+ end.then do
244
+ status.retweet!
245
+ publish(Event::Notification.new(:message, 'Successfully retweeted: @%s "%s"' % [
246
+ status.user.screen_name, status.text
247
+ ]))
248
+ end.catch do |reason|
249
+ message =
250
+ case reason
251
+ when Twitter::Error::AlreadyRetweeted
252
+ 'The status is already retweeted'
253
+ when Twitter::Error::NotFound
254
+ 'The status is not found'
255
+ when Twitter::Error::Forbidden
256
+ if status.user.id == user_id # when the status is mine
257
+ 'You cannot retweet your own status'
258
+ else # when the status is not mine
259
+ 'The status is protected'
260
+ end
261
+ else
262
+ raise e
263
+ end
264
+ publish(Event::Notification.new(:error, "Retweet attempt failed: #{message}"))
265
+ end.catch(&show_error)
266
+ end
267
+
268
+ def saved_search
269
+ send_request do
270
+ rest_client.saved_searches
271
+ end
272
+ end
273
+
274
+ def search(query)
275
+ send_request do
276
+ rest_client.search(query, count: 100).attrs[:statuses]
277
+ end.then do |statuses|
278
+ statuses
279
+ .map(&Twitter::Tweet.method(:new))
280
+ .map(&Status.method(:new))
281
+ end
282
+ end
283
+
284
+ def show_status(status_id)
285
+ send_request do
286
+ rest_client.status(status_id)
287
+ end.then do |status|
288
+ Status.new(status)
289
+ end
290
+ end
291
+
292
+ def show_user(query)
293
+ send_request_without_catch do
294
+ rest_client.user(query)
295
+ end.catch do |reason|
296
+ case reason
297
+ when Twitter::Error::NotFound
298
+ nil
299
+ else
300
+ raise reason
301
+ end
302
+ end.catch(&show_error).then do |user|
303
+ user.nil? ? nil : User.new(user)
304
+ end
305
+ end
306
+
307
+ def unblock(*user_ids)
308
+ send_request do
309
+ rest_client.unblock(*user_ids)
310
+ end.then do |users|
311
+ users.each do |user|
312
+ Friendship.unblock(self.user_id, user.id)
313
+ end
314
+ end
315
+ end
316
+
317
+ def unfavorite(status)
318
+ fail ArgumentError,
319
+ 'argument must be an instance of Status class' unless status.is_a? Status
320
+
321
+ send_request do
322
+ rest_client.unfavorite(status.id)
323
+ end.then do
324
+ status.unfavorite!
325
+ publish(Event::Notification.new(:message, 'Successfully unliked: @%s "%s"' % [
326
+ status.user.screen_name, status.text
327
+ ]))
328
+ end
329
+ end
330
+
331
+ def unfollow(*user_ids)
332
+ send_request do
333
+ rest_client.unfollow(*user_ids)
334
+ end.then do |users|
335
+ users.each do |user|
336
+ Friendship.unfollow(self.user_id, user.id)
337
+ end
338
+ end
339
+ end
340
+
341
+ def unmute(user_ids)
342
+ send_request do
343
+ rest_client.unmute(*user_ids)
344
+ end.then do |users|
345
+ users.each do |user|
346
+ Friendship.unmute(self.user_id, user.id)
347
+ end
348
+ end
349
+ end
350
+
351
+ def user_timeline(user_id)
352
+ send_request do
353
+ rest_client.user_timeline(user_id, count: 200)
354
+ end.then do |statuses|
355
+ statuses
356
+ .select(&@mute_filter)
357
+ .map(&Status.method(:new))
358
+ end
359
+ end
360
+
361
+ def rest_client
362
+ @rest_client ||= Twitter::REST::Client.new do |config|
363
+ config.consumer_key = CONSUMER_KEY
364
+ config.consumer_secret = CONSUMER_SECRET
365
+ config.access_token = @access_token
366
+ config.access_token_secret = @access_token_secret
367
+ end
368
+ end
369
+
370
+ def send_request(&block)
371
+ send_request_without_catch(&block).catch(&show_error)
372
+ end
373
+
374
+ def send_request_without_catch(&block)
375
+ Promise.new do |resolve, reject|
376
+ begin
377
+ resolve.(block.call)
378
+ rescue Twitter::Error => reason
379
+ reject.(reason)
380
+ end
381
+ end
382
+ end
383
+
384
+ private
385
+
386
+ def direct_message_manager
387
+ @direct_message_manager ||= DirectMessageManager.new(self)
388
+ end
389
+
390
+ def show_error
391
+ proc do |e|
392
+ case e
393
+ when Twitter::Error
394
+ publish(Event::Notification.new(:error, "Failed to send request: #{e.message}"))
395
+ else
396
+ raise e
397
+ end
398
+ end
399
+ end
400
+ end
401
+ end
data/lib/twterm/screen.rb CHANGED
@@ -1,6 +1,10 @@
1
+ require 'twterm/event/screen/resize'
2
+ require 'twterm/subscriber'
3
+
1
4
  module Twterm
2
5
  class Screen
3
6
  include Singleton
7
+ include Subscriber
4
8
  include Curses
5
9
 
6
10
  def initialize
@@ -11,6 +15,8 @@ module Twterm
11
15
  stdscr.keypad(true)
12
16
  start_color
13
17
  use_default_colors
18
+
19
+ subscribe(Event::Screen::Resize, :resize)
14
20
  end
15
21
 
16
22
  def refresh
@@ -19,19 +25,6 @@ module Twterm
19
25
  Notifier.instance.show
20
26
  end
21
27
 
22
- def resize
23
- return if closed?
24
-
25
- resizeterm(`tput lines`.to_i, `tput cols`.to_i)
26
- @screen.resize(`tput lines`.to_i, `tput cols`.to_i)
27
-
28
- TabManager.instance.resize
29
- TabManager.instance.each_tab(&:resize)
30
- Notifier.instance.resize
31
- FilterQueryWindow.instance.resize
32
- refresh
33
- end
34
-
35
28
  def respond_to_key(key)
36
29
  case key
37
30
  when ?n
@@ -58,6 +51,16 @@ module Twterm
58
51
 
59
52
  private
60
53
 
54
+ def resize(event)
55
+ return if closed?
56
+
57
+ lines, cols = event.lines, event.cols
58
+ resizeterm(lines, cols)
59
+ @screen.resize(lines, cols)
60
+
61
+ refresh
62
+ end
63
+
61
64
  def scan
62
65
  App.instance.reset_interruption_handler
63
66
 
data/lib/twterm/status.rb CHANGED
@@ -113,10 +113,15 @@ module Twterm
113
113
  end
114
114
 
115
115
  def update!(tweet)
116
+ return self if recently_updated?
117
+
116
118
  @retweet_count = tweet.retweet_count
117
119
  @favorite_count = tweet.favorite_count
118
120
  @retweeted = tweet.retweeted?
119
121
  @favorited = tweet.favorited?
122
+
123
+ @updated_at = Time.now
124
+
120
125
  self
121
126
  end
122
127
 
@@ -135,7 +140,7 @@ module Twterm
135
140
  cond = -> (status) { status.touched_at > Time.now - MAX_CACHED_TIME }
136
141
  statuses = all.select(&cond)
137
142
  status_ids = statuses.map(&:id)
138
- @@instances = status_ids.zip(statuses).to_h
143
+ @@instances = Hash[status_ids.zip(statuses)]
139
144
  end
140
145
 
141
146
  def self.delete(id)
@@ -159,5 +164,11 @@ module Twterm
159
164
  instance = find(tweet.id)
160
165
  instance.nil? ? super : instance.update!(tweet)
161
166
  end
167
+
168
+ private
169
+
170
+ def recently_updated?
171
+ !@updated_at.nil? && @updated_at + 60 > Time.now
172
+ end
162
173
  end
163
174
  end
@@ -0,0 +1,103 @@
1
+ require 'twterm/event/favorite'
2
+ require 'twterm/event/notification'
3
+ require 'twterm/event/status/mention'
4
+ require 'twterm/event/status/timeline'
5
+ require 'twterm/publisher'
6
+
7
+ module Twterm
8
+ module StreamingClient
9
+ include Publisher
10
+
11
+ CONSUMER_KEY = 'vLNSVFgXclBJQJRZ7VLMxL9lA'.freeze
12
+ CONSUMER_SECRET = 'OFLKzrepRG2p1hq0nUB9j2S9ndFQoNTPheTpmOY0GYw55jGgS5'.freeze
13
+
14
+ def connect_user_stream
15
+ streaming_client.stop_stream
16
+
17
+ @streaming_thread = Thread.new do
18
+ begin
19
+ publish(Event::Notification.new(:message, 'Trying to connect to Twitter...'))
20
+ streaming_client.userstream
21
+ rescue EventMachine::ConnectionError
22
+ publish(Event::Notification.new(:error, 'Connection failed'))
23
+ sleep 30
24
+ retry
25
+ end
26
+ end
27
+ end
28
+
29
+ def initialize_user_stream
30
+ return if user_stream_initialized?
31
+
32
+ streaming_client.on_friends do
33
+ user_stream_connected!
34
+ end
35
+
36
+ streaming_client.on_timeline_status do |tweet|
37
+ status = Status.new(tweet)
38
+ publish(Event::Status::Timeline.new(status))
39
+ publish(Event::Status::Mention.new(status)) if status.text.include?('@%s' % screen_name)
40
+ end
41
+
42
+ streaming_client.on_delete do |status_id|
43
+ publish(Event::StatusDeleted.new(status_id))
44
+ end
45
+
46
+ streaming_client.on_event(:favorite) do |event|
47
+ user = User.new(Twitter::User.new(event[:source]))
48
+ status = Status.new(Twitter::Status.new(event[:target]))
49
+
50
+ event = Event::Favorite.new(user, status, self)
51
+ publish(event)
52
+ end
53
+
54
+ streaming_client.on_event(:follow) do |event|
55
+ source = User.new(Twitter::User.new(event[:source]))
56
+ target = User.new(Twitter::User.new(event[:target]))
57
+
58
+ event = Event::Follow.new(source, target, self)
59
+ publish(:followed, event)
60
+ end
61
+
62
+ streaming_client.on_no_data_received do
63
+ user_stream_disconnected!
64
+ connect_user_stream
65
+ end
66
+
67
+ user_stream_initialized!
68
+ end
69
+
70
+ private
71
+
72
+ def streaming_client
73
+ @streaming_client ||= TweetStream::Client.new(
74
+ consumer_key: CONSUMER_KEY,
75
+ consumer_secret: CONSUMER_SECRET,
76
+ oauth_token: @access_token,
77
+ oauth_token_secret: @access_token_secret,
78
+ auth_method: :oauth
79
+ )
80
+ end
81
+
82
+ def user_stream_connected?
83
+ @user_stream_connected || false
84
+ end
85
+
86
+ def user_stream_connected!
87
+ publish(Event::Notification.new(:message, 'Connection established')) unless user_stream_connected?
88
+ @user_stream_connected = true
89
+ end
90
+
91
+ def user_stream_disconnected!
92
+ @user_stream_connected = false
93
+ end
94
+
95
+ def user_stream_initialized?
96
+ @user_stream_initialized || false
97
+ end
98
+
99
+ def user_stream_initialized!
100
+ @user_stream_initialized = true
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,33 @@
1
+ require 'twterm/event_dispatcher'
2
+
3
+ module Twterm
4
+ module Subscriber
5
+ def subscribe(event, callback = nil, &block)
6
+ cb = if callback.is_a?(Proc)
7
+ callback
8
+ elsif callback.is_a?(Symbol)
9
+ if self.respond_to?(callback, true)
10
+ self.method(callback)
11
+ else
12
+ callback.to_proc
13
+ end
14
+ elsif callback.nil?
15
+ block
16
+ end
17
+
18
+ EventDispatcher.instance.register_subscription(object_id, event, cb)
19
+ end
20
+
21
+ def unsubscribe(event = nil)
22
+ EventDispatcher.instance.unregister_subscription(object_id, event)
23
+
24
+ self
25
+ end
26
+
27
+ def self.included(base)
28
+ base.instance_eval do
29
+ private :subscribe, :unsubscribe
30
+ end
31
+ end
32
+ end
33
+ end
@@ -1,7 +1,11 @@
1
+ require 'twterm/event/screen/resize'
2
+ require 'twterm/subscriber'
3
+
1
4
  module Twterm
2
5
  module Tab
3
- module Base
6
+ class Base
4
7
  include Curses
8
+ include Subscriber
5
9
 
6
10
  attr_reader :window
7
11
  attr_accessor :title
@@ -11,11 +15,14 @@ module Twterm
11
15
  end
12
16
 
13
17
  def close
18
+ unsubscribe
14
19
  window.close
15
20
  end
16
21
 
17
22
  def initialize
18
23
  @window = stdscr.subwin(stdscr.maxy - 5, stdscr.maxx, 3, 0)
24
+
25
+ subscribe(Event::Screen::Resize, :resize)
19
26
  end
20
27
 
21
28
  def refresh
@@ -37,11 +44,6 @@ module Twterm
37
44
  end
38
45
  end
39
46
 
40
- def resize
41
- window.resize(stdscr.maxy - 5, stdscr.maxx)
42
- window.move(3, 0)
43
- end
44
-
45
47
  def respond_to_key(_)
46
48
  fail NotImplementedError, 'respond_to_key method must be implemented'
47
49
  end
@@ -65,6 +67,11 @@ module Twterm
65
67
  )
66
68
  end
67
69
 
70
+ def resize(event)
71
+ window.resize(stdscr.maxy - 5, stdscr.maxx)
72
+ window.move(3, 0)
73
+ end
74
+
68
75
  def update
69
76
  fail NotImplementedError, 'update method must be implemented'
70
77
  end