twterm 1.3.0 → 2.0.0.beta1

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 (121) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +14 -18
  3. data/bin/twterm +1 -1
  4. data/lib/twterm/app.rb +120 -30
  5. data/lib/twterm/client.rb +10 -13
  6. data/lib/twterm/completion_mamanger.rb +11 -6
  7. data/lib/twterm/direct_message.rb +6 -28
  8. data/lib/twterm/direct_message_composer.rb +10 -5
  9. data/lib/twterm/direct_message_manager.rb +5 -6
  10. data/lib/twterm/event/notification/abstract_notification.rb +27 -0
  11. data/lib/twterm/event/notification/error.rb +13 -0
  12. data/lib/twterm/event/notification/info.rb +13 -0
  13. data/lib/twterm/event/notification/success.rb +13 -0
  14. data/lib/twterm/event/notification/warning.rb +13 -0
  15. data/lib/twterm/event_dispatcher.rb +1 -1
  16. data/lib/twterm/extensions/array.rb +5 -0
  17. data/lib/twterm/extensions/enumerator/lazy.rb +3 -0
  18. data/lib/twterm/extensions/string.rb +0 -4
  19. data/lib/twterm/friendship.rb +1 -85
  20. data/lib/twterm/image/between.rb +31 -0
  21. data/lib/twterm/image/blank_line.rb +21 -0
  22. data/lib/twterm/image/bold.rb +31 -0
  23. data/lib/twterm/image/brackets.rb +21 -0
  24. data/lib/twterm/image/color.rb +45 -0
  25. data/lib/twterm/image/empty.rb +21 -0
  26. data/lib/twterm/image/horizontal_sequential_image.rb +48 -0
  27. data/lib/twterm/image/parens.rb +21 -0
  28. data/lib/twterm/image/string_image.rb +38 -0
  29. data/lib/twterm/image/vertical_sequential_image.rb +43 -0
  30. data/lib/twterm/image.rb +107 -0
  31. data/lib/twterm/key_mapper/abstract_key_mapper.rb +51 -0
  32. data/lib/twterm/key_mapper/app_key_mapper.rb +13 -0
  33. data/lib/twterm/key_mapper/cursor_key_mapper.rb +13 -0
  34. data/lib/twterm/key_mapper/general_key_mapper.rb +18 -0
  35. data/lib/twterm/key_mapper/no_such_command.rb +20 -0
  36. data/lib/twterm/key_mapper/no_such_key.rb +16 -0
  37. data/lib/twterm/key_mapper/status_key_mapper.rb +18 -0
  38. data/lib/twterm/key_mapper/tab_key_mapper.rb +31 -0
  39. data/lib/twterm/key_mapper.rb +127 -0
  40. data/lib/twterm/list.rb +0 -31
  41. data/lib/twterm/notifier.rb +7 -7
  42. data/lib/twterm/repository/abstract_entity_repository.rb +41 -0
  43. data/lib/twterm/repository/abstract_expirable_entity_repository.rb +35 -0
  44. data/lib/twterm/repository/abstract_repository.rb +64 -0
  45. data/lib/twterm/repository/direct_message_repository.rb +14 -0
  46. data/lib/twterm/repository/friendship_repository.rb +108 -0
  47. data/lib/twterm/repository/hashtag_repository.rb +39 -0
  48. data/lib/twterm/repository/list_repository.rb +14 -0
  49. data/lib/twterm/repository/status_repository.rb +36 -0
  50. data/lib/twterm/repository/user_repository.rb +22 -0
  51. data/lib/twterm/rest_client.rb +107 -63
  52. data/lib/twterm/screen.rb +21 -15
  53. data/lib/twterm/search_query_window.rb +139 -0
  54. data/lib/twterm/status.rb +14 -108
  55. data/lib/twterm/streaming_client.rb +13 -12
  56. data/lib/twterm/tab/base.rb +48 -8
  57. data/lib/twterm/tab/direct_message/conversation.rb +53 -52
  58. data/lib/twterm/tab/direct_message/conversation_list.rb +46 -45
  59. data/lib/twterm/tab/dumpable.rb +3 -3
  60. data/lib/twterm/tab/key_assignments_cheatsheet.rb +58 -57
  61. data/lib/twterm/tab/loadable.rb +20 -0
  62. data/lib/twterm/tab/new/list.rb +32 -43
  63. data/lib/twterm/tab/new/search.rb +31 -44
  64. data/lib/twterm/tab/new/start.rb +44 -55
  65. data/lib/twterm/tab/new/user.rb +15 -12
  66. data/lib/twterm/tab/rate_limit_status.rb +84 -0
  67. data/lib/twterm/tab/scrollable.rb +39 -19
  68. data/lib/twterm/tab/searchable.rb +133 -0
  69. data/lib/twterm/tab/statuses/base.rb +139 -129
  70. data/lib/twterm/tab/statuses/conversation.rb +26 -15
  71. data/lib/twterm/tab/statuses/favorites.rb +10 -8
  72. data/lib/twterm/tab/statuses/home.rb +10 -9
  73. data/lib/twterm/tab/statuses/list_timeline.rb +12 -8
  74. data/lib/twterm/tab/statuses/mentions.rb +17 -11
  75. data/lib/twterm/tab/statuses/search.rb +8 -5
  76. data/lib/twterm/tab/statuses/user_timeline.rb +11 -8
  77. data/lib/twterm/tab/user_list_management.rb +109 -0
  78. data/lib/twterm/tab/user_tab.rb +125 -126
  79. data/lib/twterm/tab/users/base.rb +39 -41
  80. data/lib/twterm/tab/users/followers.rb +9 -6
  81. data/lib/twterm/tab/users/friends.rb +9 -6
  82. data/lib/twterm/tab_manager.rb +64 -40
  83. data/lib/twterm/tweetbox.rb +18 -13
  84. data/lib/twterm/uri_opener.rb +2 -1
  85. data/lib/twterm/user.rb +2 -110
  86. data/lib/twterm/version.rb +1 -1
  87. data/lib/twterm/view.rb +30 -0
  88. data/lib/twterm.rb +3 -9
  89. data/spec/fixtures/status.json +107 -0
  90. data/spec/fixtures/user.json +102 -0
  91. data/spec/spec_helper.rb +7 -0
  92. data/spec/supports/shared_examples/abstract_key_mapper.rb +17 -0
  93. data/spec/twterm/extension/enumerator/lazy_spec.rb +11 -0
  94. data/spec/twterm/friendship_spec.rb +0 -102
  95. data/spec/twterm/image/blank_line_spec.rb +11 -0
  96. data/spec/twterm/image/brackets_spec.rb +12 -0
  97. data/spec/twterm/image/color_spec.rb +22 -0
  98. data/spec/twterm/image/empry_spec.rb +11 -0
  99. data/spec/twterm/image/horizontal_sequential_image_spec.rb +15 -0
  100. data/spec/twterm/image/parens_spec.rb +12 -0
  101. data/spec/twterm/image/string_image_spec.rb +12 -0
  102. data/spec/twterm/image/vertical_sequential_image_spec.rb +14 -0
  103. data/spec/twterm/image_spec.rb +65 -0
  104. data/spec/twterm/key_mapper/abstract_key_mapper_spec.rb +21 -0
  105. data/spec/twterm/key_mapper/app_key_mapper_spec.rb +7 -0
  106. data/spec/twterm/key_mapper/status_key_mapper_spec.rb +7 -0
  107. data/spec/twterm/key_mapper/tab_key_mapper_spec.rb +7 -0
  108. data/spec/twterm/repository/friendship_repository_spec.rb +108 -0
  109. data/spec/twterm/status_spec.rb +58 -0
  110. data/spec/twterm/user_spec.rb +94 -0
  111. data/twterm.gemspec +13 -10
  112. metadata +129 -35
  113. data/lib/twterm/event/notification.rb +0 -33
  114. data/lib/twterm/extensions/integer.rb +0 -5
  115. data/lib/twterm/filter_query_window.rb +0 -91
  116. data/lib/twterm/filterable_list.rb +0 -41
  117. data/lib/twterm/history/base.rb +0 -21
  118. data/lib/twterm/history/hashtag.rb +0 -13
  119. data/lib/twterm/history/savable.rb +0 -37
  120. data/lib/twterm/history/screen_name.rb +0 -11
  121. data/lib/twterm/promise.rb +0 -143
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 224b8dc2d3619a2042816816273a0526a7cb9666
4
- data.tar.gz: f1e33882f1154a83dfb5c744d1d613f4844aba48
3
+ metadata.gz: 064c6b973d192af1687649528910f091550c24ed
4
+ data.tar.gz: e7c65c16eb7c113e68b3f8aa9d03fcfafbe703d4
5
5
  SHA512:
6
- metadata.gz: 57087dc1e7f65520af3ab90af85e7d8511855ca40696709173e8797aa2d1a004fba09dd2eebcb6ce8dd03cceaf03d530a8f5ad827a6ed318e05fb5940db644ee
7
- data.tar.gz: 204684237b545673c87f0edb02fbf80b7c077095b59531ba5b1c43ff1055ccee7585354d9612dee02cf54fa88ebbaca72d250f81418c2d4a3c8d6153d1aa63ae
6
+ metadata.gz: eaa76b8503cd6749d4df7ac6e261ebeb13b1a59cef56d5aba5e327e0946013e46a180a3c5d424cbb8590adba4440e91be3234b78e91564b35e6555afa942c9e2
7
+ data.tar.gz: 6c92404ae8e51b1867efe30d93bf5bbd2a51ee4dfb06d6e02d8449913222b5d6bda82f7a98fac416718ac19bfad75743e6a428c54fd86fd816e6e0f968eed679
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # twterm
2
2
 
3
- A full-featured CLI Twitter client
3
+ A full-featured TUI Twitter client
4
4
 
5
5
  ## Screenshot
6
6
 
@@ -8,8 +8,8 @@ A full-featured CLI Twitter client
8
8
 
9
9
  ## Requirements
10
10
 
11
- - Ruby (>= 2.1, compiled with C Curses and Readline)
12
- - C curses
11
+ - Ruby (>= 2.1, compiled with ncurses and Readline)
12
+ - ncurses
13
13
  - Readline
14
14
 
15
15
  ## Installation
@@ -26,25 +26,21 @@ To launch twterm, just type in your console:
26
26
  $ twterm
27
27
  ```
28
28
 
29
- ### Basic key assignments
29
+ ### Default key assignments
30
+
31
+ Key assignments can be configured by editing `~/.twterm/keys.toml`
30
32
 
31
33
  key | operation
32
34
  --- | ---
33
- `F` | add to favorite
34
- `d` `C-d` | scroll down
35
- `h` `C-b` `←` | show previous tab
36
- `j` `C-n` `↓` | move down
37
- `k` `C-p` `↑` | move up
38
- `l` `C-f` `→` | show next tab
39
- `n` | compose new tweet
40
- `N` | open new tab
41
- `r` | reply
42
- `R` | retweet
35
+ `h` `←` | previous tab
36
+ `j` `↓` | move down
37
+ `k` `↑` | move up
38
+ `l` `→` | next tab
39
+ `^N` | new tweet
40
+ `^T` | new tab
43
41
  `w` | close current tab
44
- `Q` | quit
45
- `?` | open key assignments cheatsheet
46
-
47
- Type `?` key to see the full list of key assignments.
42
+ `F10` | quit
43
+ `F1` | key assignments cheatsheet
48
44
 
49
45
  ## License
50
46
 
data/bin/twterm CHANGED
@@ -6,5 +6,5 @@ if ARGV.count == 1 && (%w(-v --version).include?(ARGV.first))
6
6
  exit
7
7
  else
8
8
  require 'twterm'
9
- Twterm::App.instance.run
9
+ Twterm::App.new.run
10
10
  end
data/lib/twterm/app.rb CHANGED
@@ -1,55 +1,120 @@
1
1
  require 'curses'
2
+
3
+ require 'twterm/completion_mamanger'
4
+ require 'twterm/direct_message_composer'
2
5
  require 'twterm/event/screen/resize'
6
+ require 'twterm/repository/direct_message_repository'
7
+ require 'twterm/repository/friendship_repository'
8
+ require 'twterm/repository/hashtag_repository'
9
+ require 'twterm/repository/list_repository'
10
+ require 'twterm/repository/status_repository'
11
+ require 'twterm/repository/user_repository'
12
+ require 'twterm/tab_manager'
13
+ require 'twterm/tweetbox'
3
14
  require 'twterm/uri_opener'
4
15
 
5
16
  module Twterm
6
17
  class App
7
18
  include Publisher
8
- include Singleton
19
+
20
+ attr_reader :screen
9
21
 
10
22
  DATA_DIR = "#{ENV['HOME']}/.twterm".freeze
11
23
 
12
- def initialize
24
+ def completion_manager
25
+ @completion_mamanger ||= CompletionManager.new(self)
26
+ end
27
+
28
+ def direct_message_composer
29
+ @direct_message_composer ||= DirectMessageComposer.new(self, client)
30
+ end
31
+
32
+ def direct_message_repository
33
+ @direct_messages_repository ||= Repository::DirectMessageRepository.new
34
+ end
35
+
36
+ def friendship_repository
37
+ @friendship_repository ||= Repository::FriendshipRepository.new
38
+ end
39
+
40
+ def hashtag_repository
41
+ @hashtag_repository ||= Repository::HashtagRepository.new
42
+ end
43
+
44
+ def list_repository
45
+ @list_repository ||= Repository::ListRepository.new
46
+ end
47
+
48
+ def run
13
49
  Dir.mkdir(DATA_DIR, 0700) unless File.directory?(DATA_DIR)
14
50
 
15
51
  Auth.authenticate_user(config) if config[:user_id].nil?
16
52
 
17
- Screen.instance
18
- FilterQueryWindow.instance
53
+ KeyMapper.instance
19
54
 
20
- timeline = Tab::Statuses::Home.new(client)
21
- TabManager.instance.add_and_show(timeline)
55
+ @screen = Screen.new(self, client)
22
56
 
23
- mentions_tab = Tab::Statuses::Mentions.new(client)
57
+ SearchQueryWindow.instance
24
58
 
25
- TabManager.instance.add(mentions_tab)
26
- TabManager.instance.recover_tabs
59
+ timeline = Tab::Statuses::Home.new(self, client)
60
+ tab_manager.add_and_show(timeline)
27
61
 
28
- Screen.instance.refresh
62
+ mentions_tab = Tab::Statuses::Mentions.new(self, client)
63
+
64
+ tab_manager.add(mentions_tab)
65
+ tab_manager.recover_tabs
66
+
67
+ screen.refresh
29
68
 
30
69
  client.connect_user_stream
31
70
 
32
71
  reset_interruption_handler
33
72
 
73
+ Signal.trap(:WINCH) { on_resize }
74
+ Scheduler.new(60) { on_resize }
75
+
34
76
  URIOpener.instance
35
77
 
36
- resize = proc do
37
- next if Curses.closed?
78
+ Scheduler.new(300) do
79
+ status_repository.expire(3600)
38
80
 
39
- lines = `tput lines`.to_i
40
- cols = `tput cols`.to_i
41
- publish(Event::Screen::Resize.new(lines, cols))
81
+ _ = status_repository.all.map { |user_id| user_repository.find(user_id) }
82
+ user_repository.expire(3600)
42
83
  end
43
84
 
44
- Signal.trap(:WINCH, &resize)
45
- Scheduler.new(60, &resize)
46
- end
85
+ direct_message_repository.before_create do |dm|
86
+ user_repository.create(dm.recipient)
87
+ user_repository.create(dm.sender)
88
+ end
47
89
 
48
- def run
49
- run_periodic_cleanup
90
+ user_repository.before_create do |user|
91
+ client_id = client.user_id
92
+
93
+ if user.following?
94
+ friendship_repository.follow(client_id, user.id)
95
+ else
96
+ friendship_repository.unfollow(client_id, user.id)
97
+ end
98
+
99
+ if user.follow_request_sent?
100
+ friendship_repository.following_requested(client_id, user.id)
101
+ else
102
+ friendship_repository.following_not_requested(client_id, user.id)
103
+ end
104
+ end
50
105
 
51
- Screen.instance.wait
52
- Screen.instance.refresh
106
+ status_repository.before_create do |tweet|
107
+ user_repository.create(tweet.user)
108
+ end
109
+
110
+ status_repository.before_create do |tweet|
111
+ tweet.hashtags.each do |hashtag|
112
+ hashtag_repository.create(hashtag.text)
113
+ end
114
+ end
115
+
116
+ screen.wait
117
+ screen.refresh
53
118
  end
54
119
 
55
120
  def register_interruption_handler(&block)
@@ -58,15 +123,31 @@ module Twterm
58
123
  end
59
124
 
60
125
  def reset_interruption_handler
61
- Signal.trap(:INT) { App.instance.quit }
126
+ Signal.trap(:INT) { quit }
62
127
  end
63
128
 
64
129
  def quit
65
130
  Curses.close_screen
66
- TabManager.instance.dump_tabs
131
+ tab_manager.dump_tabs
67
132
  exit
68
133
  end
69
134
 
135
+ def status_repository
136
+ @status_repository ||= Repository::StatusRepository.new
137
+ end
138
+
139
+ def tab_manager
140
+ @tab_manager ||= TabManager.new(self, client)
141
+ end
142
+
143
+ def tweetbox
144
+ @tweetbox = Tweetbox.new(self, client)
145
+ end
146
+
147
+ def user_repository
148
+ @user_repository ||= Repository::UserRepository.new
149
+ end
150
+
70
151
  private
71
152
 
72
153
  def client
@@ -74,7 +155,15 @@ module Twterm
74
155
  config[:user_id].to_i,
75
156
  config[:screen_name],
76
157
  config[:access_token],
77
- config[:access_token_secret]
158
+ config[:access_token_secret],
159
+ {
160
+ friendship: friendship_repository,
161
+ direct_message: direct_message_repository,
162
+ hashtag: hashtag_repository,
163
+ list: list_repository,
164
+ status: status_repository,
165
+ user: user_repository,
166
+ }
78
167
  )
79
168
  end
80
169
 
@@ -82,11 +171,12 @@ module Twterm
82
171
  @config ||= Config.new
83
172
  end
84
173
 
85
- def run_periodic_cleanup
86
- Scheduler.new(300) do
87
- Status.cleanup
88
- User.cleanup
89
- end
174
+ def on_resize
175
+ return if Curses.closed?
176
+
177
+ lines = `tput lines`.to_i
178
+ cols = `tput cols`.to_i
179
+ publish(Event::Screen::Resize.new(lines, cols))
90
180
  end
91
181
  end
92
182
  end
data/lib/twterm/client.rb CHANGED
@@ -8,12 +8,17 @@ module Twterm
8
8
 
9
9
  attr_reader :user_id, :screen_name
10
10
 
11
- @@instances = []
12
-
13
- def initialize(user_id, screen_name, access_token, access_token_secret)
11
+ def initialize(user_id, screen_name, access_token, access_token_secret, repositories)
14
12
  @user_id, @screen_name = user_id, screen_name
15
13
  @access_token, @access_token_secret = access_token, access_token_secret
16
14
 
15
+ @friendship_repository = repositories[:friendship]
16
+ @direct_message_repository = repositories[:direct_message]
17
+ @hashtag_repository = repositories[:hashtag]
18
+ @list_repository = repositories[:list]
19
+ @status_repository = repositories[:status]
20
+ @user_repository = repositories[:user]
21
+
17
22
  @callbacks = {}
18
23
 
19
24
  @mute_filter = -> _ { true }
@@ -26,18 +31,10 @@ module Twterm
26
31
  end
27
32
 
28
33
  direct_message_manager
29
-
30
- @@instances << self
31
34
  end
32
35
 
33
- def self.new(user_id, screen_name, token, secret)
34
- detector = -> (instance) { instance.user_id == user_id }
35
- instance = @@instances.find(&detector)
36
- instance.nil? ? super : instance
37
- end
36
+ private
38
37
 
39
- def self.current
40
- @@instances[0]
41
- end
38
+ attr_reader :friendship_repository, :direct_message_repository, :hashtag_repository, :list_repository, :status_repository, :user_repository
42
39
  end
43
40
  end
@@ -1,8 +1,8 @@
1
1
  module Twterm
2
2
  class CompletionManager
3
- include Singleton
3
+ def initialize(app)
4
+ @app = app
4
5
 
5
- def initialize
6
6
  Readline.basic_word_break_characters = " \t\n\"\\'`$><=;|&{("
7
7
  Readline.completion_case_fold = false
8
8
  end
@@ -12,12 +12,12 @@ module Twterm
12
12
 
13
13
  Readline.completion_proc = proc do |str|
14
14
  if str.start_with?('#')
15
- History::Hashtag.instance.history
15
+ app.hashtag_repository.all
16
16
  .map { |tag| "##{tag}" }
17
17
  .select { |tag| tag.start_with?(str) }
18
18
  elsif str.start_with?('@')
19
- History::ScreenName.instance.history
20
- .map { |name| "@#{name}" }
19
+ app.user_repository.all
20
+ .map { |user| "@#{user.screen_name}" }
21
21
  .select { |name| name.start_with?(str) }
22
22
  else
23
23
  []
@@ -29,9 +29,14 @@ module Twterm
29
29
  Readline.completion_append_character = ''
30
30
 
31
31
  Readline.completion_proc = proc do |str|
32
- History::ScreenName.instance.history
32
+ app.user_repository.all
33
+ .map { |user| user.screen_name }
33
34
  .select { |name| name.start_with?(str) }
34
35
  end
35
36
  end
37
+
38
+ private
39
+
40
+ attr_reader :app
36
41
  end
37
42
  end
@@ -3,15 +3,11 @@ require 'twterm/utils'
3
3
 
4
4
  module Twterm
5
5
  class DirectMessage
6
- attr_reader :id, :created_at, :recipient, :sender, :text
7
-
8
- @@instances = {}
6
+ attr_reader :id, :created_at, :recipient_id, :sender_id, :text
9
7
 
10
8
  def initialize(message)
11
9
  @id = message.id
12
10
  update!(message)
13
-
14
- @@instances[id] = self
15
11
  end
16
12
 
17
13
  def ==(other)
@@ -23,18 +19,10 @@ module Twterm
23
19
  @created_at.strftime(format)
24
20
  end
25
21
 
26
- def matches?(q)
27
- [
28
- sender.name,
29
- sender.screen_name,
30
- text
31
- ].map(&:downcase).any? { |x| x.include?(q) }
32
- end
33
-
34
22
  def update!(message)
35
23
  @created_at = message.created_at.dup.localtime
36
- @recipient = User.new(message.recipient)
37
- @sender = User.new(message.sender)
24
+ @recipient_id = message.recipient.id
25
+ @sender_id = message.sender.id
38
26
  @text = message.text
39
27
 
40
28
  self
@@ -43,12 +31,10 @@ module Twterm
43
31
  class Conversation
44
32
  include Utils
45
33
 
46
- attr_reader :collocutor, :messages
34
+ attr_reader :collocutor_id, :messages
47
35
 
48
- def initialize(collocutor)
49
- check_type User, collocutor
50
-
51
- @collocutor = collocutor
36
+ def initialize(collocutor_id)
37
+ @collocutor_id = collocutor_id
52
38
  @messages = []
53
39
  end
54
40
 
@@ -59,14 +45,6 @@ module Twterm
59
45
  self
60
46
  end
61
47
 
62
- def matches?(q)
63
- [
64
- collocutor.screen_name,
65
- collocutor.name,
66
- preview
67
- ].map(&:downcase).any? { |x| x.include?(q.downcase) }
68
- end
69
-
70
48
  def preview
71
49
  messages.sort_by(&:created_at).last.text.gsub("\n", ' ')
72
50
  end
@@ -4,12 +4,15 @@ require 'twterm/utils'
4
4
 
5
5
  module Twterm
6
6
  class DirectMessageComposer
7
- include Singleton
8
7
  include Readline
9
8
  include Curses
10
9
  include Publisher
11
10
  include Utils
12
11
 
12
+ def initialize(app, client)
13
+ @app, @client = app, client
14
+ end
15
+
13
16
  def compose(recipient)
14
17
  check_type User, recipient
15
18
 
@@ -18,7 +21,7 @@ module Twterm
18
21
  resetter = proc do
19
22
  reset_prog_mode
20
23
  sleep 0.1
21
- Screen.instance.refresh
24
+ app.screen.refresh
22
25
  end
23
26
 
24
27
  thread = Thread.new do
@@ -26,7 +29,7 @@ module Twterm
26
29
 
27
30
  puts "\nCompose new message to @%s:" % recipient.screen_name
28
31
 
29
- CompletionManager.instance.set_default_mode!
32
+ app.completion_manager.set_default_mode!
30
33
 
31
34
  loop do
32
35
  line = (readline('> ', true) || '').strip
@@ -46,7 +49,7 @@ module Twterm
46
49
  send(recipient) unless text.empty?
47
50
  end
48
51
 
49
- App.instance.register_interruption_handler do
52
+ app.register_interruption_handler do
50
53
  thread.kill
51
54
  clear
52
55
  puts "\nCanceled"
@@ -58,12 +61,14 @@ module Twterm
58
61
 
59
62
  private
60
63
 
64
+ attr_reader :app, :client
65
+
61
66
  def clear
62
67
  @text = ''
63
68
  end
64
69
 
65
70
  def send(recipient)
66
- Client.current.create_direct_message(recipient, text)
71
+ client.create_direct_message(recipient, text)
67
72
  clear
68
73
  end
69
74
 
@@ -21,22 +21,21 @@ module Twterm
21
21
  Scheduler.new(300) { fetch }
22
22
  end
23
23
 
24
- def add(collocutor, message)
25
- check_type User, collocutor
24
+ def add(collocutor_id, message)
26
25
  check_type DirectMessage, message
27
26
 
28
- @conversations[collocutor.id] ||= DirectMessage::Conversation.new(collocutor)
29
- @conversations[collocutor.id] << message
27
+ @conversations[collocutor_id] ||= DirectMessage::Conversation.new(collocutor_id)
28
+ @conversations[collocutor_id] << message
30
29
  end
31
30
 
32
31
  def fetch
33
32
  client.direct_messages_received.then do |messages|
34
- messages.each { |m| add(m.sender, m) }
33
+ messages.each { |m| add(m.sender_id, m) }
35
34
  publish(Event::DirectMessage::Fetched.new)
36
35
  end
37
36
 
38
37
  client.direct_messages_sent.then do |messages|
39
- messages.each { |m| add(m.recipient, m) }
38
+ messages.each { |m| add(m.recipient_id, m) }
40
39
  publish(Event::DirectMessage::Fetched.new)
41
40
  end
42
41
  end
@@ -0,0 +1,27 @@
1
+ require 'twterm/event/base'
2
+
3
+ module Twterm
4
+ module Event
5
+ module Notification
6
+ class AbstractNotification < 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
+ message: String
18
+ }
19
+ end
20
+
21
+ def color
22
+ raise NotImplementedError, 'color method must be overridden'
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,13 @@
1
+ require 'twterm/event/notification/abstract_notification'
2
+
3
+ module Twterm
4
+ module Event
5
+ module Notification
6
+ class Error < AbstractNotification
7
+ def color
8
+ [:white, :red]
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ require 'twterm/event/notification/abstract_notification'
2
+
3
+ module Twterm
4
+ module Event
5
+ module Notification
6
+ class Info < AbstractNotification
7
+ def color
8
+ [:black, :cyan]
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ require 'twterm/event/notification/abstract_notification'
2
+
3
+ module Twterm
4
+ module Event
5
+ module Notification
6
+ class Success < AbstractNotification
7
+ def color
8
+ [:black, :green]
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ require 'twterm/event/notification/abstract_notification'
2
+
3
+ module Twterm
4
+ module Event
5
+ module Notification
6
+ class Warning < AbstractNotification
7
+ def color
8
+ [:black, :yellow]
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -12,7 +12,7 @@ module Twterm
12
12
 
13
13
  def dispatch(event)
14
14
  @subscriptions
15
- .select { |s| s.event == event.class }
15
+ .select { |s| event.is_a?(s.event) }
16
16
  .map(&:callback)
17
17
  .each { |cb| cb.call(event) }
18
18
 
@@ -0,0 +1,5 @@
1
+ class Array
2
+ def intersperse(x)
3
+ ([x] * length).zip(self).flatten(1).drop(1)
4
+ end
5
+ end
@@ -3,7 +3,10 @@ class Enumerator
3
3
  def scan(initial, sym = nil)
4
4
  acc = initial
5
5
 
6
+ @_scan_has_yielded_initial_value = false
7
+
6
8
  Enumerator::Lazy.new(self) do |y, x|
9
+ y << initial && @_scan_has_yielded_initial_value = true unless @_scan_has_yielded_initial_value
7
10
  acc = sym.nil? ? yield(acc, x) : sym.to_proc.call(acc, x)
8
11
  y << acc
9
12
  end
@@ -3,10 +3,6 @@ class String
3
3
  each_char.map { |c| c.bytesize == 1 ? 1 : 2 }.reduce(0, &:+)
4
4
  end
5
5
 
6
- def matches?(query)
7
- downcase.include?(query.downcase)
8
- end
9
-
10
6
  def split_by_width(width)
11
7
  cnt = 0
12
8
  str = ''