twterm 2.1.0 → 2.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 (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,33 @@
1
+ require 'twterm/event/notification/abstract_notification'
2
+
3
+ module Twterm
4
+ module Event
5
+ module Notification
6
+ class ListMemberAdded < AbstractNotification
7
+ # @param [Twterm::List] list
8
+ def initialize(list)
9
+ @list = list
10
+ end
11
+
12
+ # @return [String] notification body
13
+ def body
14
+ list.description
15
+ end
16
+
17
+ # @return [String] notification title
18
+ def title
19
+ "You've been added to #{list.full_name}"
20
+ end
21
+
22
+ # @return [String] notification url
23
+ def url
24
+ list.url
25
+ end
26
+
27
+ private
28
+
29
+ attr_reader :list
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,35 @@
1
+ require 'twterm/event/notification/abstract_notification'
2
+
3
+ module Twterm
4
+ module Event
5
+ module Notification
6
+ class Mention < AbstractNotification
7
+ # @param [Twterm::Status] status
8
+ # @param [Twterm::User] user
9
+ def initialize(status, user)
10
+ @status = status
11
+ @user = user
12
+ end
13
+
14
+ # @return [String] notification body
15
+ def body
16
+ status.text
17
+ end
18
+
19
+ # @return [String] notification title
20
+ def title
21
+ "@#{user.screen_name} has replied to your tweet"
22
+ end
23
+
24
+ # @return [String] notification url
25
+ def url
26
+ status.url
27
+ end
28
+
29
+ private
30
+
31
+ attr_reader :status, :user
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,35 @@
1
+ require 'twterm/event/notification/abstract_notification'
2
+
3
+ module Twterm
4
+ module Event
5
+ module Notification
6
+ class Quote < AbstractNotification
7
+ # @param [Twterm::Status] status
8
+ # @param [Twterm::User] user
9
+ def initialize(status, user)
10
+ @status = status
11
+ @user = user
12
+ end
13
+
14
+ # @return [String] notification body
15
+ def body
16
+ status.text
17
+ end
18
+
19
+ # @return [String] notification title
20
+ def title
21
+ "@#{user.screen_name} has quoted your tweet"
22
+ end
23
+
24
+ # @return [String] notification url
25
+ def url
26
+ status.url
27
+ end
28
+
29
+ private
30
+
31
+ attr_reader :status, :user
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,35 @@
1
+ require 'twterm/event/notification/abstract_notification'
2
+
3
+ module Twterm
4
+ module Event
5
+ module Notification
6
+ class Retweet < AbstractNotification
7
+ # @param [Twterm::Status] status
8
+ # @param [Twterm::User] user
9
+ def initialize(status, user)
10
+ @status = status
11
+ @user = user
12
+ end
13
+
14
+ # @return [String] notification body
15
+ def body
16
+ status.text
17
+ end
18
+
19
+ # @return [String] notification title
20
+ def title
21
+ "@#{user.screen_name} has retweeted your tweet"
22
+ end
23
+
24
+ # @return [String] notification url
25
+ def url
26
+ status.url
27
+ end
28
+
29
+ private
30
+
31
+ attr_reader :status, :user
32
+ end
33
+ end
34
+ end
35
+ end
@@ -23,6 +23,10 @@ module Twterm
23
23
  @mappings[key]
24
24
  end
25
25
 
26
+ def self.commands
27
+ self::DEFAULT_MAPPINGS.keys
28
+ end
29
+
26
30
  def to_h
27
31
  @dict
28
32
  end
@@ -49,10 +53,6 @@ module Twterm
49
53
  raise NoSuchKey.new(key)
50
54
  end
51
55
  end
52
-
53
- def self.commands
54
- self::DEFAULT_MAPPINGS.keys
55
- end
56
56
  end
57
57
  end
58
58
  end
data/lib/twterm/list.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  module Twterm
2
2
  class List
3
- attr_reader :id, :name, :slug, :full_name, :mode, :description, :member_count, :subscriber_count
3
+ attr_reader :id, :name, :slug, :full_name, :mode, :description, :member_count, :subscriber_count, :url
4
4
 
5
5
  def ==(other)
6
6
  other.is_a?(self.class) && id == other.id
@@ -19,6 +19,7 @@ module Twterm
19
19
  @description = list.description.is_a?(Twitter::NullObject) ? '' : list.description
20
20
  @member_count = list.member_count
21
21
  @subscriber_count = list.subscriber_count
22
+ @url = list.url
22
23
 
23
24
  self
24
25
  end
@@ -0,0 +1,81 @@
1
+ require 'twterm/subscriber'
2
+ require 'twterm/event/message/abstract_message'
3
+ require 'twterm/event/screen/resize'
4
+
5
+ module Twterm
6
+ class MessageWindow
7
+ include Singleton
8
+ include Curses
9
+ include Subscriber
10
+
11
+ def initialize
12
+ @window = stdscr.subwin(1, stdscr.maxx, stdscr.maxy - 2, 0)
13
+ @queue = Queue.new
14
+
15
+ subscribe(Event::Message::AbstractMessage) do |e|
16
+ queue(e)
17
+ end
18
+
19
+ subscribe(Event::Screen::Resize, :resize)
20
+
21
+ Thread.new do
22
+ while message = @queue.pop # rubocop:disable Lint/AssignmentInCondition:
23
+ show(message)
24
+ sleep 3
25
+ show
26
+ end
27
+ end
28
+ end
29
+
30
+ def show_info(message)
31
+ @queue.push(Event::Message::Info.new(message))
32
+ self
33
+ end
34
+
35
+ def show(message = nil)
36
+ loop do
37
+ break unless closed?
38
+ sleep 0.5
39
+ end
40
+
41
+ @window.clear
42
+
43
+ if message.is_a?(Event::Message::AbstractMessage)
44
+ fg_color, bg_color =
45
+ case message
46
+ when Event::Message::Error
47
+ [:white, :red]
48
+ when Event::Message::Info
49
+ [:black, :cyan]
50
+ when Event::Message::Success
51
+ [:black, :green]
52
+ when Event::Message::Warning
53
+ [:black, :yellow]
54
+ end
55
+
56
+ @window.with_color(fg_color, bg_color) do
57
+ @window.setpos(0, 0)
58
+ @window.addstr(' ' * @window.maxx)
59
+ @window.setpos(0, 1)
60
+ time = message.time.strftime('[%H:%M:%S]')
61
+ body = message.body.gsub("\n", ' ')
62
+ @window.addstr("#{time} #{body}")
63
+ end
64
+ end
65
+
66
+ @window.refresh
67
+ end
68
+
69
+ private
70
+
71
+ def queue(message)
72
+ @queue.push(message)
73
+ self
74
+ end
75
+
76
+ def resize(_event)
77
+ @window.resize(1, stdscr.maxx)
78
+ @window.move(stdscr.maxy - 2, 0)
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,16 @@
1
+ module Twterm
2
+ module NotificationBackend
3
+ # @abstract
4
+ class AbstractNotificationBackend
5
+ # @abstract
6
+ # @param [Twterm::Event::Notification::AbstractNotification] _notification a notification to show
7
+ def notify(_notification)
8
+ raise NotImplementedError, '`notify` method must be implemented'
9
+ end
10
+
11
+ private
12
+
13
+ attr_reader :app
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,20 @@
1
+ require 'curses'
2
+
3
+ require 'twterm/event/message/info'
4
+ require 'twterm/notification_backend/abstract_notification_backend'
5
+ require 'twterm/publisher'
6
+
7
+ module Twterm
8
+ module NotificationBackend
9
+ class InlineBackend < AbstractNotificationBackend
10
+ include Publisher
11
+
12
+ # @param [Twterm::Event::Notification::AbstractNotification] notification
13
+ # @return [void]
14
+ def notify(notification)
15
+ message = Event::Message::Info.new(notification.fallback)
16
+ publish(message)
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,23 @@
1
+ require 'terminal-notifier'
2
+
3
+ require 'twterm/notification_backend/abstract_notification_backend'
4
+
5
+ module Twterm
6
+ module NotificationBackend
7
+ class TerminalNotifierBackend < AbstractNotificationBackend
8
+ # @param [Twterm::Event::Notification::AbstractNotification] notification notification to display via terminal-notifier
9
+ def notify(notification)
10
+ opts = {
11
+ title: 'twterm',
12
+ subtitle: notification.title,
13
+ group: Process.pid,
14
+ sound: 'Purr',
15
+ }
16
+
17
+ opts[:open] = notification.url unless notification.url.nil?
18
+
19
+ TerminalNotifier.notify(notification.body, opts)
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,17 @@
1
+ require 'shellwords'
2
+
3
+ require 'twterm/notification_backend/abstract_notification_backend'
4
+
5
+ module Twterm
6
+ module NotificationBackend
7
+ class TmuxBackend < AbstractNotificationBackend
8
+ # @param [Twterm::Event::Notification::AbstractNotification] notification notification to display in tmux status line
9
+ # @return [void]
10
+ def notify(notification)
11
+ `tmux set-option display-time 3000`
12
+ `tmux set-option message-style fg=black,bg=green`
13
+ `tmux display #{Shellwords.escape(" [twterm] #{notification.fallback.gsub("\n", ' ')}")}`
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,36 @@
1
+ require 'twterm/event/notification/abstract_notification'
2
+ require 'twterm/notification_backend/inline_backend'
3
+ require 'twterm/notification_backend/terminal_notifier_backend'
4
+ require 'twterm/notification_backend/tmux_backend'
5
+ require 'twterm/subscriber'
6
+
7
+ module Twterm
8
+ class NotificationDispatcher
9
+ include Subscriber
10
+
11
+ # @param [Twterm::Preferences] preferences
12
+ def initialize(preferences)
13
+ @preferences = preferences
14
+
15
+ @backends = {
16
+ inline: NotificationBackend::InlineBackend.new,
17
+ terminal_notifier: NotificationBackend::TerminalNotifierBackend.new,
18
+ tmux: NotificationBackend::TmuxBackend.new,
19
+ }
20
+
21
+ subscribe(Event::Notification::AbstractNotification) { |n| dispatch(n) }
22
+ end
23
+
24
+ private
25
+
26
+ attr_reader :backends, :preferences
27
+
28
+ # @param [Twterm::Notification::AbstractNotification] notification notification to dispatch to backends
29
+ # @return [void]
30
+ def dispatch(notification)
31
+ backends.keys.each do |key|
32
+ backends[key].notify(notification) if preferences[:notification_backend, key]
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,82 @@
1
+ require 'toml'
2
+
3
+ module Twterm
4
+ class PersistableConfigurationProxy
5
+ # @param [Twterm::AbstractPersistableConfiguration] instance A configuration to persist
6
+ # @param [String] filepath File path to persist the given configuration
7
+ def initialize(instance, filepath)
8
+ @instance = instance
9
+ @filepath = filepath
10
+ end
11
+
12
+ def [](*args)
13
+ instance.[](*args)
14
+ end
15
+
16
+ def []=(*args)
17
+ instance.[]=(*args)
18
+ persist!
19
+ end
20
+
21
+ def self.getc
22
+ system('stty raw -echo')
23
+ STDIN.getc
24
+ ensure
25
+ system('stty -raw echo')
26
+ end
27
+
28
+ # Loads a configuration file from and returns a proxy.
29
+ # Falls back to the default value when the specified file does not exist.
30
+ #
31
+ # @param [Class] klass Must be a subclass of {Twterm::AbstractPersistableConfiguration}
32
+ # @param [String] filepath File path to load configuration from
33
+ # @return [Twterm::PersistableConfigurationProxy] a configuration proxy
34
+ def self.load_from_file!(klass, filepath)
35
+ config = TOML.load_file(filepath, symbolize_keys: true)
36
+ new(klass.new(config), filepath).migrate!
37
+ rescue Errno::ENOENT
38
+ new(klass.default, filepath)
39
+ rescue TOML::ParseError, TOML::ValueOverwriteError => e
40
+ msg =
41
+ case e
42
+ when TOML::ParseError
43
+ "Your configuration file could not be parsed"
44
+ when TOML::ValueOverwriteError
45
+ "`#{e.key}` is declared more than once"
46
+ end
47
+
48
+ warn <<-EOS
49
+ \e[1mCould not load the configuration file: #{filepath}\e[0m
50
+ (#{msg})
51
+
52
+ Falling back to the default key assignments
53
+
54
+ Check the syntax and edit the file manually,
55
+ or remove it and launch twterm again to restore
56
+
57
+ Press any key to continue
58
+ EOS
59
+
60
+ getc
61
+
62
+ new(klass.default, filepath)
63
+ end
64
+
65
+ # @return [self]
66
+ def migrate!
67
+ instance.complete_missing_items!
68
+ persist!
69
+
70
+ self
71
+ end
72
+
73
+ private
74
+
75
+ attr_reader :filepath, :instance
76
+
77
+ def persist!
78
+ hash = TOML.dump(instance.to_h).gsub("\n[", "\n\n[")
79
+ File.open(filepath, 'w', 0644) { |f| f.write(hash) }
80
+ end
81
+ end
82
+ end