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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: a822edd0e5719ca957ab183b1859bd6ec01113ca
4
- data.tar.gz: ecec2ab95f599a575226d0a3239f1b93ef63a4f9
2
+ SHA256:
3
+ metadata.gz: bce2d4276a631793204a44044b38afaa43f02d2c16bcc6daf68653288d1c5e12
4
+ data.tar.gz: e1307f6b9623eb96f2659406377888283d5a912fd30caf0d20737852a62e9b9c
5
5
  SHA512:
6
- metadata.gz: 1608685c830f8f614bc6549b498c271b7851deb05ee2e7054b62857ff89dae64f0d17f9f3d1fc22338493644aa35c9d4beb78c7ee2f6054eecd939108255d216
7
- data.tar.gz: f188471c3974d031c257b2a6fd9169926d1350b663d4b71ce4ee180d20e6d63a2a61657c1755e019c01ca38f1402493d386b53cb4e4e90b752051969e184ecf0
6
+ metadata.gz: 43c9cce69344a7e7cc8107793bc79f133ffe1ff134ec422eacbae4ae449661c0b57e7f6b15a241bb5d69db4c58d6bc22065badc9b85f1203bc29e130d8711fb9
7
+ data.tar.gz: fdad1dfb3aff8e7f66a94d00db63e5bd3d1e274072c17664e23ebb3de0a38dc26ebc2f9229f8172b14e84c1d71971cea05aba7ca9133eb7f537f03c33dbc8508
data/.travis.yml CHANGED
@@ -5,5 +5,8 @@ rvm:
5
5
  - 2.2
6
6
  - 2.3.0
7
7
  - 2.4.0
8
+ - 2.5.0
8
9
 
9
- script: bundle exec rspec
10
+ before_install: gem update --system
11
+
12
+ script: bundle exec rubocop --parallel --fail-level warning && bundle exec rspec
data/lib/twterm.rb CHANGED
@@ -26,7 +26,7 @@ require 'twterm/extensions/string'
26
26
  require 'twterm/search_query_window'
27
27
  require 'twterm/friendship'
28
28
  require 'twterm/list'
29
- require 'twterm/notifier'
29
+ require 'twterm/message_window'
30
30
  require 'twterm/screen'
31
31
  require 'twterm/scheduler'
32
32
  require 'twterm/status'
@@ -0,0 +1,118 @@
1
+ module Twterm
2
+ # @abstract
3
+ class AbstractPersistableConfiguration
4
+ # @param [Hash<Symbol, Object>] configuration A hash object to initialize configuration
5
+ def initialize(configuration = {})
6
+ @configuration = configuration
7
+ end
8
+
9
+ # Gets a value associated to the given key.
10
+ # @abstract
11
+ def [](*)
12
+ raise NotImplementedError, '`#[]` method must be implemented'
13
+ end
14
+
15
+ # Sets the given value to the given key.
16
+ # @abstract
17
+ def []=(*)
18
+ raise NotImplementedError, '`#[]=` method must be implemented'
19
+ end
20
+
21
+ # Returns the default instance for the configuration.
22
+ # @abstract
23
+ def self.default
24
+ raise NotImplementedError, '`.default` method must be implemented'
25
+ end
26
+
27
+ # @return [Boolean]
28
+ def self.has_same_structure?
29
+ true
30
+ end
31
+
32
+ # @return [self]
33
+ def complete_missing_items!
34
+ traverse(self.class.default.to_h) do |path, default_value|
35
+ value = dig(configuration, path)
36
+
37
+ if value.nil? || !(dig!(self.class.structure, path) === value)
38
+ bury!(path, default_value)
39
+ end
40
+ end
41
+
42
+ self
43
+ end
44
+
45
+ # Converts to a hash object.
46
+ #
47
+ # @abstract
48
+ # @return [Hash] Configuration as a hash
49
+ def to_h
50
+ raise NotImplementedError, '`#to_h` method must be implemented'
51
+ end
52
+
53
+ # A tree-like object representing the structure of the configuration.
54
+ # Each node must respond to {#===} method which determines whether a value is acceptable.
55
+ #
56
+ # @abstract
57
+ # @return [Hash] A tree-like hash object
58
+ def self.structure
59
+ raise NotImplementedError, '`.structure` method must be implemented'
60
+ end
61
+
62
+ private
63
+
64
+ attr_reader :configuration
65
+
66
+ def bury!(path, value)
67
+ go = lambda do |hash, rest_path|
68
+ k, *ks = rest_path
69
+
70
+ if ks.empty?
71
+ hash[k] = value
72
+ else
73
+ hash[k] = {} unless hash[k].is_a?(Hash)
74
+ go.call(hash[k], ks)
75
+ end
76
+ end
77
+
78
+ go.call(configuration, path)
79
+
80
+ configuration
81
+ end
82
+
83
+ def dig(hash, path)
84
+ k, *ks = path
85
+ ks.empty? ? hash[k] : dig(hash[k], ks)
86
+ rescue NoMethodError
87
+ nil
88
+ end
89
+
90
+ def dig!(hash, path)
91
+ k, *ks = path
92
+ ks.empty? ? hash[k] : dig(hash[k], ks)
93
+ rescue NoMethodError
94
+ raise ArgumentError, "path #{path} not found for hash #{hash}"
95
+ end
96
+
97
+ # Traverses tree-like {Hash} object and calls the given block on every node with its path
98
+ #
99
+ # @yieldparam [Array<Symbol>] path the path of the current node
100
+ # @yieldparam [Object] v the value of the current node
101
+ # @yieldreturn [void]
102
+ def traverse(tree, &f)
103
+ go = lambda do |subtree, current_path|
104
+ subtree.each do |k, v|
105
+ path = [*current_path, k]
106
+
107
+ if v.is_a?(Hash)
108
+ go.call(v, path)
109
+ else
110
+ f.call(path, v)
111
+ end
112
+ end
113
+ end
114
+
115
+ go.call(tree, [])
116
+ end
117
+ end
118
+ end
data/lib/twterm/app.rb CHANGED
@@ -2,7 +2,12 @@ require 'curses'
2
2
 
3
3
  require 'twterm/completion_manager'
4
4
  require 'twterm/direct_message_composer'
5
+ require 'twterm/environment'
5
6
  require 'twterm/event/screen/resize'
7
+ require 'twterm/message_window'
8
+ require 'twterm/notification_dispatcher'
9
+ require 'twterm/persistable_configuration_proxy'
10
+ require 'twterm/preferences'
6
11
  require 'twterm/repository/direct_message_repository'
7
12
  require 'twterm/repository/friendship_repository'
8
13
  require 'twterm/repository/hashtag_repository'
@@ -17,10 +22,20 @@ module Twterm
17
22
  class App
18
23
  include Publisher
19
24
 
20
- attr_reader :screen
25
+ attr_reader :environment, :preferences, :screen
21
26
 
22
27
  DATA_DIR = "#{ENV['HOME']}/.twterm".freeze
23
28
 
29
+ def initialize
30
+ @environment = Environment.new
31
+ @preferences = Preferences.default
32
+ end
33
+
34
+ def load_preferences_from_file!
35
+ @preferences = PersistableConfigurationProxy
36
+ .load_from_file!(Preferences, "#{DATA_DIR}/preferences.toml")
37
+ end
38
+
24
39
  def completion_manager
25
40
  @completion_manager ||= CompletionManager.new(self)
26
41
  end
@@ -50,11 +65,16 @@ module Twterm
50
65
 
51
66
  Auth.authenticate_user(config) if config[:user_id].nil?
52
67
 
68
+ load_preferences_from_file!
69
+
53
70
  KeyMapper.instance
54
71
 
55
72
  @screen = Screen.new(self, client)
56
73
 
57
74
  SearchQueryWindow.instance
75
+ MessageWindow.instance
76
+
77
+ @notification_dispatcher = NotificationDispatcher.new(preferences)
58
78
 
59
79
  timeline = Tab::Statuses::Home.new(self, client)
60
80
  tab_manager.add_and_show(timeline)
@@ -0,0 +1,38 @@
1
+ require 'terminal-notifier'
2
+
3
+ module Twterm
4
+ class Environment
5
+ def initialize
6
+ @uname = `uname`.strip
7
+
8
+ @terminal_notifier_available = TerminalNotifier.available?
9
+ @with_eog = system('which eog 2>&1 >/dev/null')
10
+ @with_tmux = system('which tmux 2>&1 >/dev/null') && !ENV['TMUX'].nil?
11
+ @with_qlmanage = system('which qlmanage 2>&1 >/dev/null')
12
+ end
13
+
14
+ def darwin?
15
+ @uname == 'Darwin'
16
+ end
17
+
18
+ def linux?
19
+ @uname == 'Linux'
20
+ end
21
+
22
+ def terminal_notifier_available?
23
+ @terminal_notifier_available
24
+ end
25
+
26
+ def with_eog?
27
+ @with_eog
28
+ end
29
+
30
+ def with_qlmanage?
31
+ @with_qlmanage
32
+ end
33
+
34
+ def with_tmux?
35
+ @with_tmux
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,23 @@
1
+ require 'twterm/event/base'
2
+
3
+ module Twterm
4
+ module Event
5
+ module Message
6
+ class AbstractMessage < Twterm::Event::Base
7
+ attr_reader :time
8
+
9
+ def initialize(message)
10
+ super(CGI.unescapeHTML(message))
11
+
12
+ @time = Time.now
13
+ end
14
+
15
+ def fields
16
+ {
17
+ body: String
18
+ }
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,10 @@
1
+ require 'twterm/event/message/abstract_message'
2
+
3
+ module Twterm
4
+ module Event
5
+ module Message
6
+ class Error < AbstractMessage
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,10 @@
1
+ require 'twterm/event/message/abstract_message'
2
+
3
+ module Twterm
4
+ module Event
5
+ module Message
6
+ class Info < AbstractMessage
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,10 @@
1
+ require 'twterm/event/message/abstract_message'
2
+
3
+ module Twterm
4
+ module Event
5
+ module Message
6
+ class Success < AbstractMessage
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,10 @@
1
+ require 'twterm/event/message/abstract_message'
2
+
3
+ module Twterm
4
+ module Event
5
+ module Message
6
+ class Warning < AbstractMessage
7
+ end
8
+ end
9
+ end
10
+ end
@@ -3,23 +3,28 @@ require 'twterm/event/base'
3
3
  module Twterm
4
4
  module Event
5
5
  module Notification
6
- class AbstractNotification < Twterm::Event::Base
7
- attr_reader :time
8
-
9
- def initialize(message)
10
- super(CGI.unescapeHTML(message))
6
+ # @abstract
7
+ class AbstractNotification < Event::Base
8
+ # @abstract
9
+ # @return [String] notification body
10
+ def body
11
+ raise NotImplementedError, '`body` must be implemented'
12
+ end
11
13
 
12
- @time = Time.now
14
+ # @return [String] a fallback text
15
+ def fallback
16
+ "#{title}: #{body}"
13
17
  end
14
18
 
15
- def fields
16
- {
17
- message: String
18
- }
19
+ # @abstract
20
+ # @return [String] notification title
21
+ def title
22
+ raise NotImplementedError, '`fallback` must be implemented'
19
23
  end
20
24
 
21
- def color
22
- raise NotImplementedError, 'color method must be overridden'
25
+ # @return [String, nil] notification URL
26
+ def url
27
+ nil
23
28
  end
24
29
  end
25
30
  end
@@ -0,0 +1,30 @@
1
+ require 'twterm/event/notification/abstract_notification'
2
+
3
+ module Twterm
4
+ module Event
5
+ module Notification
6
+ class DirectMessage < AbstractNotification
7
+ # @param [Twterm::DirectMessage] message
8
+ # @param [Twterm::User] user
9
+ def initialize(message, user)
10
+ @message = message
11
+ @user = user
12
+ end
13
+
14
+ # @return [String] notification body
15
+ def body
16
+ message.text
17
+ end
18
+
19
+ # @return [String] notification title
20
+ def title
21
+ "@#{user.screen_name} has sent you a message"
22
+ end
23
+
24
+ private
25
+
26
+ attr_reader :message, :user
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,35 @@
1
+ require 'twterm/event/notification/abstract_notification'
2
+
3
+ module Twterm
4
+ module Event
5
+ module Notification
6
+ class Like < 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 liked 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,33 @@
1
+ require 'twterm/event/notification/abstract_notification'
2
+
3
+ module Twterm
4
+ module Event
5
+ module Notification
6
+ class Follow < AbstractNotification
7
+ # @param [Twterm::User] user
8
+ def initialize(user)
9
+ @user = user
10
+ end
11
+
12
+ # @return [String] notification body
13
+ def body
14
+ user.description
15
+ end
16
+
17
+ # @return [String] notification title
18
+ def title
19
+ "@#{user.screen_name} has followed you"
20
+ end
21
+
22
+ # @return [String] notification url
23
+ def url
24
+ user.url
25
+ end
26
+
27
+ private
28
+
29
+ attr_reader :user
30
+ end
31
+ end
32
+ end
33
+ end