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,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