twterm 2.1.0 → 2.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. checksums.yaml +5 -5
  2. data/.travis.yml +4 -1
  3. data/lib/twterm.rb +1 -1
  4. data/lib/twterm/abstract_persistable_configuration.rb +118 -0
  5. data/lib/twterm/app.rb +21 -1
  6. data/lib/twterm/environment.rb +38 -0
  7. data/lib/twterm/event/message/abstract_message.rb +23 -0
  8. data/lib/twterm/event/message/error.rb +10 -0
  9. data/lib/twterm/event/message/info.rb +10 -0
  10. data/lib/twterm/event/message/success.rb +10 -0
  11. data/lib/twterm/event/message/warning.rb +10 -0
  12. data/lib/twterm/event/notification/abstract_notification.rb +17 -12
  13. data/lib/twterm/event/notification/direct_message.rb +30 -0
  14. data/lib/twterm/event/notification/favorite.rb +35 -0
  15. data/lib/twterm/event/notification/follow.rb +33 -0
  16. data/lib/twterm/event/notification/list_member_added.rb +33 -0
  17. data/lib/twterm/event/notification/mention.rb +35 -0
  18. data/lib/twterm/event/notification/quote.rb +35 -0
  19. data/lib/twterm/event/notification/retweet.rb +35 -0
  20. data/lib/twterm/key_mapper/abstract_key_mapper.rb +4 -4
  21. data/lib/twterm/list.rb +2 -1
  22. data/lib/twterm/message_window.rb +81 -0
  23. data/lib/twterm/notification_backend/abstract_notification_backend.rb +16 -0
  24. data/lib/twterm/notification_backend/inline_backend.rb +20 -0
  25. data/lib/twterm/notification_backend/terminal_notifier_backend.rb +23 -0
  26. data/lib/twterm/notification_backend/tmux_backend.rb +17 -0
  27. data/lib/twterm/notification_dispatcher.rb +36 -0
  28. data/lib/twterm/persistable_configuration_proxy.rb +82 -0
  29. data/lib/twterm/preferences.rb +66 -0
  30. data/lib/twterm/repository/abstract_expirable_entity_repository.rb +1 -1
  31. data/lib/twterm/rest_client.rb +12 -14
  32. data/lib/twterm/scheduler.rb +1 -1
  33. data/lib/twterm/screen.rb +1 -1
  34. data/lib/twterm/search_query_window.rb +2 -2
  35. data/lib/twterm/streaming_client.rb +54 -17
  36. data/lib/twterm/tab/base.rb +2 -3
  37. data/lib/twterm/tab/new/list.rb +2 -4
  38. data/lib/twterm/tab/new/start.rb +10 -0
  39. data/lib/twterm/tab/new/user.rb +2 -2
  40. data/lib/twterm/tab/preferences/index.rb +74 -0
  41. data/lib/twterm/tab/preferences/notification_backend.rb +85 -0
  42. data/lib/twterm/tab/scrollable.rb +0 -1
  43. data/lib/twterm/tab/searchable.rb +7 -7
  44. data/lib/twterm/tab/user_list_management.rb +3 -3
  45. data/lib/twterm/tab/user_tab.rb +6 -8
  46. data/lib/twterm/tab_manager.rb +4 -4
  47. data/lib/twterm/tweetbox.rb +1 -1
  48. data/lib/twterm/uri_opener.rb +2 -2
  49. data/lib/twterm/user.rb +2 -1
  50. data/lib/twterm/version.rb +1 -1
  51. data/twterm.gemspec +4 -2
  52. metadata +59 -14
  53. data/lib/twterm/event/favorite.rb +0 -18
  54. data/lib/twterm/event/follow.rb +0 -17
  55. data/lib/twterm/event/notification/error.rb +0 -13
  56. data/lib/twterm/event/notification/info.rb +0 -13
  57. data/lib/twterm/event/notification/success.rb +0 -13
  58. data/lib/twterm/event/notification/warning.rb +0 -13
  59. data/lib/twterm/notifier.rb +0 -82
@@ -0,0 +1,66 @@
1
+ require 'toml'
2
+
3
+ require 'twterm/abstract_persistable_configuration'
4
+
5
+ module Twterm
6
+ class Preferences < AbstractPersistableConfiguration
7
+ def initialize(preferences)
8
+ super(preferences)
9
+ end
10
+
11
+ # @param [Symbol] cat
12
+ # @param [Symbol] key
13
+ def [](cat, key)
14
+ validate_key!(cat, key)
15
+
16
+ configuration[cat][key]
17
+ end
18
+
19
+ def []=(cat, key, value)
20
+ validate_key!(cat, key)
21
+
22
+ configuration[cat][key] = value
23
+ end
24
+
25
+ # Returns an instance having the default value
26
+ #
27
+ # @return [Twterm::Preferences] an instance having the default value
28
+ def self.default
29
+ new({
30
+ notification_backend: {
31
+ inline: true,
32
+ terminal_notifier: false,
33
+ tmux: false,
34
+ },
35
+ })
36
+ end
37
+
38
+ # @return [Hash]
39
+ def to_h
40
+ configuration
41
+ end
42
+
43
+ # @return [Hash]
44
+ def self.structure
45
+ bool = -> x { x == true || x == false }
46
+
47
+ {
48
+ notification_backend: {
49
+ inline: bool,
50
+ terminal_notifier: bool,
51
+ tmux: bool,
52
+ },
53
+ }
54
+ end
55
+
56
+ private
57
+
58
+ alias_method :preferences, :configuration
59
+
60
+ # @raise [ArgumentError]
61
+ def validate_key!(cat, key)
62
+ raise ArgumentError, "no such category: #{cat}" unless configuration.has_key?(cat)
63
+ raise ArgumentError, "no such key: #{cat}.#{key}" unless configuration[cat].has_key?(key)
64
+ end
65
+ end
66
+ end
@@ -40,7 +40,7 @@ module Twterm
40
40
  raise NotImplementedError, '`garbage_collection_event_class` must be implemented'
41
41
  end
42
42
 
43
- def should_keep?(instance)
43
+ def should_keep?(_instance)
44
44
  raise NotImplementedError, '`should_keep?` method must be implemented'
45
45
  end
46
46
 
@@ -2,7 +2,7 @@ require 'concurrent'
2
2
  require 'twterm/direct_message'
3
3
  require 'twterm/direct_message_manager'
4
4
  require 'twterm/publisher'
5
- require 'twterm/event/notification/success'
5
+ require 'twterm/event/message/success'
6
6
 
7
7
  module Twterm
8
8
  module RESTClient
@@ -32,7 +32,7 @@ module Twterm
32
32
  msg = direct_message_repository.create(message)
33
33
  direct_message_manager.add(recipient.id, msg)
34
34
  publish(Event::DirectMessage::Fetched.new)
35
- publish(Event::Notification::Success.new('Your message to @%s has been sent' % recipient.screen_name))
35
+ publish(Event::Message::Success.new('Your message to @%s has been sent' % recipient.screen_name))
36
36
  end
37
37
  end
38
38
 
@@ -56,11 +56,11 @@ module Twterm
56
56
  send_request_without_catch do
57
57
  rest_client.destroy_status(status.id)
58
58
  publish(Event::Status::Delete.new(status.id))
59
- publish(Event::Notification::Success.new('Your tweet has been deleted'))
59
+ publish(Event::Message::Success.new('Your tweet has been deleted'))
60
60
  end.catch do |reason|
61
61
  case reason
62
62
  when Twitter::Error::NotFound, Twitter::Error::Forbidden
63
- publish(Event::Notification::Error.new('You cannot destroy that status'))
63
+ publish(Event::Message::Error.new('You cannot destroy that status'))
64
64
  else
65
65
  raise reason
66
66
  end
@@ -75,7 +75,7 @@ module Twterm
75
75
  end.then do |tweet, *_|
76
76
  status_repository.create(tweet)
77
77
 
78
- publish(Event::Notification::Success.new('Successfully liked: @%s "%s"' % [
78
+ publish(Event::Message::Success.new('Successfully liked: @%s "%s"' % [
79
79
  tweet.user.screen_name, status.text
80
80
  ]))
81
81
  end
@@ -201,7 +201,7 @@ module Twterm
201
201
  end
202
202
  end.catch do |e|
203
203
  case e
204
- when Twitter::Error::TooManyRequests
204
+ when Twitter::Error::TooManyRequests # rubocop:disable Lint/EmptyWhen
205
205
  # do nothing
206
206
  else
207
207
  raise e
@@ -250,7 +250,7 @@ module Twterm
250
250
  rest_client.update(text, options)
251
251
  end
252
252
  .then do |status|
253
- publish(Event::Notification::Success.new('Your tweet has been posted'))
253
+ publish(Event::Message::Success.new('Your tweet has been posted'))
254
254
 
255
255
  status
256
256
  end
@@ -273,7 +273,7 @@ module Twterm
273
273
  end.then do |tweet, *_|
274
274
  status_repository.create(tweet)
275
275
 
276
- publish(Event::Notification::Success.new('Successfully retweeted: @%s "%s"' % [
276
+ publish(Event::Message::Success.new('Successfully retweeted: @%s "%s"' % [
277
277
  tweet.user.screen_name, status.text
278
278
  ]))
279
279
  end.catch do |reason|
@@ -288,7 +288,7 @@ module Twterm
288
288
  else
289
289
  raise e
290
290
  end
291
- publish(Event::Notification::Error.new("Retweet attempt failed: #{message}"))
291
+ publish(Event::Message::Error.new("Retweet attempt failed: #{message}"))
292
292
  end.catch(&show_error)
293
293
  end
294
294
 
@@ -350,9 +350,7 @@ module Twterm
350
350
  end.then do |tweet, *_|
351
351
  status_repository.create(tweet)
352
352
 
353
- publish(Event::Notification::Success.new('Successfully unliked: @%s "%s"' % [
354
- tweet.user.screen_name, status.text
355
- ]))
353
+ publish(Event::Message::Success.new("Successfully unliked @#{tweet.user.screen_name}'s tweet", sutatus.text))
356
354
  end
357
355
  end
358
356
 
@@ -389,7 +387,7 @@ module Twterm
389
387
  .then do |tweet|
390
388
  status = status_repository.create(tweet)
391
389
 
392
- publish(Event::Notification::Success.new('Successfully unretweeted: @%s "%s"' % [
390
+ publish(Event::Message::Success.new('Successfully unretweeted: @%s "%s"' % [
393
391
  tweet.user.screen_name, status.text
394
392
  ]))
395
393
 
@@ -434,7 +432,7 @@ module Twterm
434
432
  proc do |e|
435
433
  case e
436
434
  when Twitter::Error
437
- publish(Event::Notification::Error.new("Failed to send request: #{e.message}"))
435
+ publish(Event::Message::Error.new("Failed to send request: #{e.message}"))
438
436
  else
439
437
  raise e
440
438
  end
@@ -25,6 +25,6 @@ class Scheduler
25
25
 
26
26
  def run
27
27
  @block.call unless @paused
28
- rescue
28
+ rescue StandardError # rubocop:disable Lint/HandleExceptions
29
29
  end
30
30
  end
data/lib/twterm/screen.rb CHANGED
@@ -24,7 +24,7 @@ module Twterm
24
24
  def refresh
25
25
  app.tab_manager.refresh_window
26
26
  app.tab_manager.current_tab.render
27
- Notifier.instance.show
27
+ MessageWindow.instance.show
28
28
  end
29
29
 
30
30
  def respond_to_key(key)
@@ -45,7 +45,7 @@ module Twterm
45
45
 
46
46
  @str.chop!
47
47
  render_current_string
48
- when 0..31
48
+ when 0..31 # rubocop:disable Lint/EmptyWhen
49
49
  # ignore control codes (\x00 - \x1f)
50
50
  else
51
51
  next if chars.empty?
@@ -116,7 +116,7 @@ module Twterm
116
116
 
117
117
  attr_reader :window
118
118
 
119
- def resize(event)
119
+ def resize(_event)
120
120
  window.resize(1, stdscr.maxx)
121
121
  window.move(stdscr.maxy - 1, 0)
122
122
  end
@@ -1,7 +1,12 @@
1
- require 'twterm/event/favorite'
2
- require 'twterm/event/follow'
3
- require 'twterm/event/notification/error'
4
- require 'twterm/event/notification/info'
1
+ require 'twterm/event/message/error'
2
+ require 'twterm/event/message/info'
3
+ require 'twterm/event/notification/direct_message'
4
+ require 'twterm/event/notification/favorite'
5
+ require 'twterm/event/notification/follow'
6
+ require 'twterm/event/notification/list_member_added'
7
+ require 'twterm/event/notification/mention'
8
+ require 'twterm/event/notification/quote'
9
+ require 'twterm/event/notification/retweet'
5
10
  require 'twterm/event/status/mention'
6
11
  require 'twterm/event/status/timeline'
7
12
  require 'twterm/publisher'
@@ -18,32 +23,64 @@ module Twterm
18
23
 
19
24
  @streaming_thread = Thread.new do
20
25
  begin
21
- publish(Event::Notification::Info.new('Trying to connect to Twitter...'))
26
+ publish(Event::Message::Info.new('Trying to connect to Twitter...'))
22
27
  streaming_client.user do |event|
23
28
  keep_alive!
24
29
 
25
30
  case event
26
31
  when Twitter::Tweet
27
32
  status = status_repository.create(event)
33
+ user = user_repository.create(event.user)
34
+
28
35
  publish(Event::Status::Timeline.new(status))
29
- publish(Event::Status::Mention.new(status)) if status.text.include?('@%s' % screen_name)
36
+
37
+ if !status.retweet? && status.text.include?('@%s' % screen_name)
38
+ publish(Event::Status::Mention.new(status))
39
+
40
+ notification = Event::Notification::Mention.new(status, user)
41
+ publish(notification)
42
+ end
43
+
44
+ if status.retweet? && event.retweeted_status.user.id == user_id
45
+ retweeted_status = status_repository.create(event.retweeted_status)
46
+ notification = Event::Notification::Retweet.new(retweeted_status, user)
47
+ publish(notification)
48
+ end
30
49
  when Twitter::Streaming::Event
31
50
  case event.name
32
51
  when :favorite
52
+ next if event.source.id == user_id
33
53
  user = user_repository.create(event.source)
34
54
  status = status_repository.create(event.target_object)
35
-
36
- event = Event::Favorite.new(user, status, self)
37
- publish(event)
55
+ notification = Event::Notification::Like.new(status, user)
56
+ publish(notification)
38
57
  when :follow
39
- source = user_repository.create(event.source)
40
- target = user_repository.create(event.target)
58
+ next if event.source.id == user_id
59
+
60
+ user = user_repository.create(event.source)
61
+ notification = Event::Notification::Follow.new(user)
62
+ publish(notification)
63
+ when :list_member_added
64
+ next if event.source.id == user_id
41
65
 
42
- event = Event::Follow.new(source, target, self)
66
+ list = list_repository.create(event.target_object)
67
+ notification = Event::Notification::ListMemberAdded.new(list)
68
+ publish(notification)
69
+ when :quoted_tweet
70
+ next if event.source.id == user_id
43
71
 
44
- publish(event)
72
+ status = status_repository.create(event.target_object)
73
+ user = user_repository.create(event.source)
74
+ notification = Event::Notification::Quote.new(status, user)
75
+ publish(notification)
45
76
  end
46
77
  when Twitter::DirectMessage
78
+ next if event.sender.id == user_id
79
+
80
+ user = user_repository.create(event.sender)
81
+ message = DirectMessage.new(event)
82
+ notification = Event::Notification::DirectMessage.new(message, user)
83
+ publish(notification)
47
84
  when Twitter::Streaming::FriendList
48
85
  user_stream_connected!
49
86
  when Twitter::Streaming::DeletedTweet
@@ -51,15 +88,15 @@ module Twterm
51
88
  end
52
89
  end
53
90
  rescue Twitter::Error::TooManyRequests
54
- publish(Event::Notification::Error.new('Rate limit exceeded'))
91
+ publish(Event::Message::Error.new('Rate limit exceeded'))
55
92
  sleep 120
56
93
  retry
57
94
  rescue Errno::ENETUNREACH, Errno::ETIMEDOUT, Resolv::ResolvError
58
- publish(Event::Notification::Error.new('Network is unavailable'))
95
+ publish(Event::Message::Error.new('Network is unavailable'))
59
96
  sleep 30
60
97
  retry
61
98
  rescue Twitter::Error => e
62
- publish(Event::Notification::Error.new(e.message))
99
+ publish(Event::Message::Error.new(e.message))
63
100
  end
64
101
  end
65
102
  end
@@ -90,7 +127,7 @@ module Twterm
90
127
  end
91
128
 
92
129
  def user_stream_connected!
93
- publish(Event::Notification::Info.new('Connection established')) unless user_stream_connected?
130
+ publish(Event::Message::Info.new('Connection established')) unless user_stream_connected?
94
131
  @user_stream_connected = true
95
132
  end
96
133
 
@@ -10,8 +10,7 @@ module Twterm
10
10
  include Curses
11
11
  include Subscriber
12
12
 
13
- attr_reader :window
14
- attr_accessor :title
13
+ attr_reader :window, :title
15
14
 
16
15
  def ==(other)
17
16
  self.equal?(other)
@@ -107,7 +106,7 @@ module Twterm
107
106
  )
108
107
  end
109
108
 
110
- def resize(event)
109
+ def resize(_event)
111
110
  window.resize(stdscr.maxy - 5, stdscr.maxx)
112
111
  window.move(3, 0)
113
112
  end
@@ -1,4 +1,4 @@
1
- require 'twterm/event/notification/info'
1
+ require 'twterm/event/message/info'
2
2
  require 'twterm/tab/base'
3
3
  require 'twterm/tab/loadable'
4
4
 
@@ -33,7 +33,7 @@ module Twterm
33
33
  @@lists || []
34
34
  end
35
35
 
36
- def matches?(list, query)
36
+ def matches?(_list, query)
37
37
  [
38
38
  other.description,
39
39
  other.full_name,
@@ -43,8 +43,6 @@ module Twterm
43
43
  def respond_to_key(key)
44
44
  return true if scroller.respond_to_key(key)
45
45
 
46
- k = KeyMapper.instance
47
-
48
46
  case key
49
47
  when 10
50
48
  return true if current_list.nil?
@@ -1,6 +1,7 @@
1
1
  require 'twterm/tab/base'
2
2
  require 'twterm/tab/direct_message/conversation_list'
3
3
  require 'twterm/tab/rate_limit_status'
4
+ require 'twterm/tab/preferences/index'
4
5
 
5
6
  module Twterm
6
7
  module Tab
@@ -24,6 +25,7 @@ module Twterm
24
25
  user_tab
25
26
  key_assignments_cheatsheet
26
27
  rate_limit_status
28
+ preferences
27
29
  ).freeze
28
30
  end
29
31
 
@@ -69,6 +71,8 @@ module Twterm
69
71
  'Key assignments cheatsheet'
70
72
  when :rate_limit_status
71
73
  'Rate limit status'
74
+ when :preferences
75
+ 'Preferences'
72
76
  end
73
77
 
74
78
  cursor - Image.whitespace - Image.string(desc)
@@ -103,6 +107,10 @@ module Twterm
103
107
  switch(Tab::KeyAssignmentsCheatsheet.new(app, client))
104
108
  end
105
109
 
110
+ def open_preferences_tab
111
+ switch(Tab::Preferences::Index.new(app, client))
112
+ end
113
+
106
114
  def perform_selected_action
107
115
  case current_item
108
116
  when :direct_messages
@@ -117,6 +125,8 @@ module Twterm
117
125
  open_key_assignments_cheatsheet
118
126
  when :rate_limit_status
119
127
  open_rate_limit_status
128
+ when :preferences
129
+ open_preferences_tab
120
130
  end
121
131
  end
122
132
 
@@ -1,6 +1,6 @@
1
1
  require 'twterm/publisher'
2
2
  require 'twterm/tab/base'
3
- require 'twterm/event/notification/error'
3
+ require 'twterm/event/message/error'
4
4
 
5
5
  module Twterm
6
6
  module Tab
@@ -33,7 +33,7 @@ module Twterm
33
33
  else
34
34
  client.show_user(screen_name).then do |user|
35
35
  if user.nil?
36
- publish(Event::Notification::Error.new('User not found'))
36
+ publish(Event::Message::Error.new('User not found'))
37
37
  tab = Tab::New::Start.new(app, client)
38
38
  else
39
39
  tab = Tab::UserTab.new(app, client, user.id)
@@ -0,0 +1,74 @@
1
+ require 'twterm/image'
2
+ require 'twterm/tab/preferences/notification_backend'
3
+ require 'twterm/preferences'
4
+ require 'twterm/publisher'
5
+ require 'twterm/tab/base'
6
+ require 'twterm/tab/scrollable'
7
+
8
+ module Twterm
9
+ module Tab
10
+ module Preferences
11
+ class Index < Tab::Base
12
+ include Scrollable
13
+
14
+ def initialize(app, client)
15
+ super(app, client)
16
+ end
17
+
18
+ def drawable_item_count
19
+ (window.maxy - 1) / 2
20
+ end
21
+
22
+ def image
23
+ drawable_items.map.with_index do |item, i|
24
+ cursor = Image.cursor(1, scroller.current_index?(i))
25
+ desc =
26
+ case item
27
+ when :notification_backend
28
+ 'Notification backend preferences'
29
+ end
30
+
31
+ cursor - Image.whitespace - Image.string(desc)
32
+ end
33
+ .intersperse(Image.blank_line)
34
+ .reduce(Image.empty) { |acc, x| acc | x }
35
+ end
36
+
37
+ def items
38
+ [
39
+ :notification_backend
40
+ ]
41
+ end
42
+
43
+ def respond_to_key(key)
44
+ return true if scroller.respond_to_key(key)
45
+
46
+ case key
47
+ when 10
48
+ open
49
+ else
50
+ return false
51
+ end
52
+
53
+ true
54
+ end
55
+
56
+ def title
57
+ 'Preferences'
58
+ end
59
+
60
+ private
61
+
62
+ def open
63
+ tab =
64
+ case scroller.current_item
65
+ when :notification_backend
66
+ Tab::Preferences::NotificationBackend.new(app, client)
67
+ end
68
+
69
+ app.tab_manager.add_and_show(tab)
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end