twterm 2.5.1 → 2.10.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 (67) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +62 -0
  3. data/.gitignore +1 -0
  4. data/Makefile +21 -0
  5. data/README.md +52 -6
  6. data/bin/twterm +1 -3
  7. data/default.nix +21 -0
  8. data/gemset.nix +4981 -0
  9. data/lib/twterm/app.rb +13 -26
  10. data/lib/twterm/client.rb +1 -6
  11. data/lib/twterm/color_manager.rb +8 -9
  12. data/lib/twterm/image.rb +31 -0
  13. data/lib/twterm/image/attr.rb +42 -0
  14. data/lib/twterm/image/bold.rb +7 -21
  15. data/lib/twterm/image/color.rb +9 -15
  16. data/lib/twterm/image/dim.rb +13 -0
  17. data/lib/twterm/image/underlined.rb +17 -0
  18. data/lib/twterm/image_builder/user_name_image_builder.rb +1 -0
  19. data/lib/twterm/key_mapper.rb +6 -8
  20. data/lib/twterm/key_mapper/abstract_key_mapper.rb +2 -2
  21. data/lib/twterm/list.rb +36 -1
  22. data/lib/twterm/message_window.rb +4 -13
  23. data/lib/twterm/persistable_configuration_proxy.rb +6 -6
  24. data/lib/twterm/preferences.rb +8 -1
  25. data/lib/twterm/rest_client.rb +0 -33
  26. data/lib/twterm/screen.rb +103 -25
  27. data/lib/twterm/search_query_window.rb +5 -13
  28. data/lib/twterm/status.rb +10 -0
  29. data/lib/twterm/tab/abstract_tab.rb +30 -16
  30. data/lib/twterm/tab/new/index.rb +0 -10
  31. data/lib/twterm/tab/new/search.rb +2 -2
  32. data/lib/twterm/tab/new/user.rb +2 -2
  33. data/lib/twterm/tab/preferences/control.rb +77 -0
  34. data/lib/twterm/tab/preferences/index.rb +6 -0
  35. data/lib/twterm/tab/scrollable.rb +1 -1
  36. data/lib/twterm/tab/searchable.rb +6 -4
  37. data/lib/twterm/tab/status_tab.rb +10 -0
  38. data/lib/twterm/tab/statuses/abstract_statuses_tab.rb +11 -6
  39. data/lib/twterm/tab/statuses/home.rb +0 -3
  40. data/lib/twterm/tab/statuses/mentions.rb +1 -0
  41. data/lib/twterm/tab/user_tab.rb +0 -9
  42. data/lib/twterm/tab_manager.rb +77 -10
  43. data/lib/twterm/tweetbox.rb +2 -3
  44. data/lib/twterm/user.rb +1 -0
  45. data/lib/twterm/version.rb +1 -1
  46. data/nix/Gemfile +3 -0
  47. data/nix/Gemfile.lock +77 -0
  48. data/nix/gemset.nix +325 -0
  49. data/shell.nix +40 -0
  50. data/spec/twterm/image/bold_spec.rb +30 -0
  51. data/spec/twterm/image/color_spec.rb +16 -0
  52. data/spec/twterm/image/dim_spec.rb +30 -0
  53. data/twterm.gemspec +13 -13
  54. metadata +46 -46
  55. data/.travis.yml +0 -12
  56. data/lib/twterm/direct_message.rb +0 -60
  57. data/lib/twterm/direct_message_composer.rb +0 -80
  58. data/lib/twterm/direct_message_manager.rb +0 -51
  59. data/lib/twterm/event/direct_message/fetched.rb +0 -10
  60. data/lib/twterm/event/notification/direct_message.rb +0 -30
  61. data/lib/twterm/event/screen/resize.rb +0 -13
  62. data/lib/twterm/event/status/timeline.rb +0 -10
  63. data/lib/twterm/repository/direct_message_repository.rb +0 -14
  64. data/lib/twterm/streaming_client.rb +0 -146
  65. data/lib/twterm/tab/direct_message/conversation.rb +0 -104
  66. data/lib/twterm/tab/direct_message/conversation_list.rb +0 -100
  67. data/spec/twterm/event/screen/resize_spec.rb +0 -11
data/lib/twterm/app.rb CHANGED
@@ -1,16 +1,13 @@
1
1
  require 'curses'
2
2
 
3
3
  require 'twterm/completion_manager'
4
- require 'twterm/direct_message_composer'
5
4
  require 'twterm/environment'
6
5
  require 'twterm/event/screen/refresh'
7
- require 'twterm/event/screen/resize'
8
6
  require 'twterm/message_window'
9
7
  require 'twterm/notification_dispatcher'
10
8
  require 'twterm/persistable_configuration_proxy'
11
9
  require 'twterm/preferences'
12
10
  require 'twterm/photo_viewer'
13
- require 'twterm/repository/direct_message_repository'
14
11
  require 'twterm/repository/friendship_repository'
15
12
  require 'twterm/repository/hashtag_repository'
16
13
  require 'twterm/repository/list_repository'
@@ -26,6 +23,12 @@ module Twterm
26
23
 
27
24
  attr_reader :environment, :preferences, :screen
28
25
 
26
+ # return [Twterm::MessageWindow]
27
+ attr_reader :message_window
28
+
29
+ # return [Twterm::SearchQueryWindow]
30
+ attr_reader :search_query_window
31
+
29
32
  DATA_DIR = "#{ENV['HOME']}/.twterm".freeze
30
33
 
31
34
  def initialize
@@ -42,14 +45,6 @@ module Twterm
42
45
  @completion_manager ||= CompletionManager.new(self)
43
46
  end
44
47
 
45
- def direct_message_composer
46
- @direct_message_composer ||= DirectMessageComposer.new(self, client)
47
- end
48
-
49
- def direct_message_repository
50
- @direct_messages_repository ||= Repository::DirectMessageRepository.new
51
- end
52
-
53
48
  def friendship_repository
54
49
  @friendship_repository ||= Repository::FriendshipRepository.new
55
50
  end
@@ -73,8 +68,8 @@ module Twterm
73
68
 
74
69
  @screen = Screen.new(self, client)
75
70
 
76
- SearchQueryWindow.instance
77
- MessageWindow.instance
71
+ @search_query_window = SearchQueryWindow.new(screen.search_query_window_window)
72
+ @message_window = MessageWindow.new(screen.message_window_window)
78
73
 
79
74
  @notification_dispatcher = NotificationDispatcher.new(preferences)
80
75
  @photo_viewer = PhotoViewer.new(preferences)
@@ -89,8 +84,6 @@ module Twterm
89
84
 
90
85
  publish(Event::Screen::Refresh.new)
91
86
 
92
- client.connect_user_stream
93
-
94
87
  reset_interruption_handler
95
88
 
96
89
  Signal.trap(:WINCH) { on_resize }
@@ -105,11 +98,6 @@ module Twterm
105
98
  user_repository.expire(3600)
106
99
  end
107
100
 
108
- direct_message_repository.before_create do |dm|
109
- user_repository.create(dm.recipient)
110
- user_repository.create(dm.sender)
111
- end
112
-
113
101
  user_repository.before_create do |user|
114
102
  client_id = client.user_id
115
103
 
@@ -160,7 +148,7 @@ module Twterm
160
148
  end
161
149
 
162
150
  def tab_manager
163
- @tab_manager ||= TabManager.new(self, client)
151
+ @tab_manager ||= TabManager.new(self, client, screen.tab_manager_window)
164
152
  end
165
153
 
166
154
  def tweetbox
@@ -181,7 +169,6 @@ module Twterm
181
169
  config[:access_token_secret],
182
170
  {
183
171
  friendship: friendship_repository,
184
- direct_message: direct_message_repository,
185
172
  hashtag: hashtag_repository,
186
173
  list: list_repository,
187
174
  status: status_repository,
@@ -195,11 +182,11 @@ module Twterm
195
182
  end
196
183
 
197
184
  def on_resize
198
- return if Curses.closed?
185
+ lines, cols = `stty size`.split(' ').map(&:to_i)
186
+
187
+ Readline.set_screen_size(lines, cols)
199
188
 
200
- lines = `tput lines`.to_i
201
- cols = `tput cols`.to_i
202
- publish(Event::Screen::Resize.new(lines, cols))
189
+ screen.resize(lines, cols) unless Curses.closed?
203
190
  end
204
191
  end
205
192
  end
data/lib/twterm/client.rb CHANGED
@@ -1,10 +1,8 @@
1
1
  require 'twterm/rest_client'
2
- require 'twterm/streaming_client'
3
2
 
4
3
  module Twterm
5
4
  class Client
6
5
  include RESTClient
7
- include StreamingClient
8
6
 
9
7
  attr_reader :user_id, :screen_name
10
8
 
@@ -13,7 +11,6 @@ module Twterm
13
11
  @access_token, @access_token_secret = access_token, access_token_secret
14
12
 
15
13
  @friendship_repository = repositories[:friendship]
16
- @direct_message_repository = repositories[:direct_message]
17
14
  @hashtag_repository = repositories[:hashtag]
18
15
  @list_repository = repositories[:list]
19
16
  @status_repository = repositories[:status]
@@ -29,12 +26,10 @@ module Twterm
29
26
  muted_user_ids.include?(status.retweeted_status.user.id))
30
27
  end
31
28
  end
32
-
33
- direct_message_manager
34
29
  end
35
30
 
36
31
  private
37
32
 
38
- attr_reader :friendship_repository, :direct_message_repository, :hashtag_repository, :list_repository, :status_repository, :user_repository
33
+ attr_reader :friendship_repository, :hashtag_repository, :list_repository, :status_repository, :user_repository
39
34
  end
40
35
  end
@@ -1,18 +1,17 @@
1
1
  module Twterm
2
2
  class ColorManager
3
3
  include Singleton
4
- include Curses
5
4
 
6
5
  COLORS = [:black, :white, :red, :green, :blue, :yellow, :cyan, :magenta, :transparent]
7
6
  CURSES_COLORS = {
8
- black: COLOR_BLACK,
9
- white: COLOR_WHITE,
10
- red: COLOR_RED,
11
- green: COLOR_GREEN,
12
- blue: COLOR_BLUE,
13
- yellow: COLOR_YELLOW,
14
- cyan: COLOR_CYAN,
15
- magenta: COLOR_MAGENTA,
7
+ black: Curses::COLOR_BLACK,
8
+ white: Curses::COLOR_WHITE,
9
+ red: Curses::COLOR_RED,
10
+ green: Curses::COLOR_GREEN,
11
+ blue: Curses::COLOR_BLUE,
12
+ yellow: Curses::COLOR_YELLOW,
13
+ cyan: Curses::COLOR_CYAN,
14
+ magenta: Curses::COLOR_MAGENTA,
16
15
  transparent: -1
17
16
  }
18
17
 
data/lib/twterm/image.rb CHANGED
@@ -3,10 +3,12 @@ require 'twterm/image/blank_line'
3
3
  require 'twterm/image/bold'
4
4
  require 'twterm/image/brackets'
5
5
  require 'twterm/image/color'
6
+ require 'twterm/image/dim'
6
7
  require 'twterm/image/empty'
7
8
  require 'twterm/image/horizontal_sequential_image'
8
9
  require 'twterm/image/parens'
9
10
  require 'twterm/image/string_image'
11
+ require 'twterm/image/underlined'
10
12
  require 'twterm/image/vertical_sequential_image'
11
13
 
12
14
  class Twterm::Image
@@ -14,6 +16,10 @@ class Twterm::Image
14
16
  @column, @line = column, line
15
17
  end
16
18
 
19
+ def _
20
+ underlined
21
+ end
22
+
17
23
  def !
18
24
  bold
19
25
  end
@@ -44,6 +50,10 @@ class Twterm::Image
44
50
  Brackets.new(self)
45
51
  end
46
52
 
53
+ def dim(on = true)
54
+ on ? Dim.new(self) : self
55
+ end
56
+
47
57
  def self.checkbox(checked)
48
58
  string(checked ? '*' : ' ').brackets
49
59
  end
@@ -101,6 +111,27 @@ class Twterm::Image
101
111
  StringImage.new(str)
102
112
  end
103
113
 
114
+ # @param items [Array<Symbol>]
115
+ # @param selected [Symbol]
116
+ #
117
+ # @return [Image]
118
+ def self.toggle_switch(items, selected)
119
+ items
120
+ .map do |item|
121
+ on = item == selected
122
+ string(item.to_s)
123
+ .bold(on)
124
+ .underlined(on)
125
+ end
126
+ .intersperse(string(' | '))
127
+ .reduce(empty) { |acc, x| acc - x }
128
+ .brackets
129
+ end
130
+
131
+ def underlined(on = true)
132
+ on ? Underlined.new(self) : self
133
+ end
134
+
104
135
  def self.whitespace
105
136
  string(' ')
106
137
  end
@@ -0,0 +1,42 @@
1
+ require 'twterm/image'
2
+
3
+ class Twterm::Image::Attr < Twterm::Image
4
+ # @param image [Twterm::Image]
5
+ def initialize(image)
6
+ super()
7
+
8
+ @image = image
9
+ end
10
+
11
+ def height
12
+ image.height
13
+ end
14
+
15
+ def render(window)
16
+ image, attr =
17
+ if image.is_a?(self.class) # fuse attributes when possible
18
+ [image.image, self.attr | image.attr]
19
+ else
20
+ [self.image, self.attr]
21
+ end
22
+
23
+ window.attron(attr)
24
+ image.at(line, column).render(window)
25
+ window.attroff(attr)
26
+ end
27
+
28
+ def width
29
+ image.width
30
+ end
31
+
32
+ protected
33
+
34
+ attr_reader :image
35
+
36
+ # @abstract
37
+ #
38
+ # @return [Integer]
39
+ def attr
40
+ raise NotImplementedError, '`attr` must be implemented'
41
+ end
42
+ end
@@ -1,31 +1,17 @@
1
+ require 'twterm/image/attr'
2
+
1
3
  module Twterm
2
4
  class Image
3
- class Bold < Twterm::Image
4
- def initialize(image)
5
- @image = image
6
- end
7
-
8
- def height
9
- image.height
10
- end
11
-
12
- def render(window)
13
- window.attron(Curses::A_BOLD)
14
- image.at(line, column).render(window)
15
- window.attroff(Curses::A_BOLD)
16
- end
17
-
5
+ class Bold < Twterm::Image::Attr
18
6
  def to_s
19
7
  "\e[1m#{image}\e[0m"
20
8
  end
21
9
 
22
- def width
23
- image.width
24
- end
10
+ protected
25
11
 
26
- private
27
-
28
- attr_reader :image
12
+ def attr
13
+ Curses::A_BOLD
14
+ end
29
15
  end
30
16
  end
31
17
  end
@@ -1,18 +1,12 @@
1
+ require 'twterm/image/attr'
2
+
1
3
  module Twterm
2
4
  class Image
3
- class Color < Twterm::Image
5
+ class Color < Twterm::Image::Attr
4
6
  def initialize(image, fg, bg = :transparent)
5
- @image, @fg, @bg = image, fg, bg
6
- end
7
-
8
- def height
9
- image.height
10
- end
7
+ super(image)
11
8
 
12
- def render(window)
13
- window.attron(Curses.color_pair(color_pair_index))
14
- image.at(line, column).render(window)
15
- window.attroff(Curses.color_pair(color_pair_index))
9
+ @fg, @bg = fg, bg
16
10
  end
17
11
 
18
12
  def to_s
@@ -29,14 +23,14 @@ module Twterm
29
23
  @bg == :transparent ? str : "\e[#{bg_colors[@bg]}m#{str}"
30
24
  end
31
25
 
32
- def width
33
- image.width
26
+ protected
27
+
28
+ def attr
29
+ Curses.color_pair(color_pair_index)
34
30
  end
35
31
 
36
32
  private
37
33
 
38
- attr_reader :image
39
-
40
34
  def color_pair_index
41
35
  Twterm::ColorManager.instance.get_color_pair_index(@fg, @bg)
42
36
  end
@@ -0,0 +1,13 @@
1
+ require 'twterm/image/attr'
2
+
3
+ class Twterm::Image::Dim < Twterm::Image::Attr
4
+ def to_s
5
+ "\e[2m#{image}\e[0m"
6
+ end
7
+
8
+ protected
9
+
10
+ def attr
11
+ Curses::A_DIM
12
+ end
13
+ end
@@ -0,0 +1,17 @@
1
+ require 'twterm/image/attr'
2
+
3
+ module Twterm
4
+ class Image
5
+ class Underlined < Twterm::Image::Attr
6
+ def to_s
7
+ "\e[4m#{image}\e[0m"
8
+ end
9
+
10
+ protected
11
+
12
+ def attr
13
+ Curses::A_UNDERLINE
14
+ end
15
+ end
16
+ end
17
+ end
@@ -1,6 +1,7 @@
1
1
  require 'twterm/image'
2
2
 
3
3
  module Twterm
4
+ # @todo Rename to `Presenter`
4
5
  module ImageBuilder
5
6
  class UserNameImageBuilder
6
7
  COLORS = [:red, :blue, :green, :cyan, :yellow, :magenta].freeze
@@ -1,4 +1,4 @@
1
- require 'toml'
1
+ require 'toml-rb'
2
2
  require 'singleton'
3
3
 
4
4
  require 'twterm/app'
@@ -105,14 +105,12 @@ module Twterm
105
105
  end
106
106
 
107
107
  def load_dict_file!
108
- dict = TOML.load_file(dict_file_path, symbolize_keys: true)
109
- rescue TOML::ParseError, TOML::ValueOverwriteError => e
108
+ dict = TomlRB.load_file(dict_file_path, symbolize_keys: true)
109
+ rescue TomlRB::ParseError => e
110
110
  first_line =
111
111
  case e
112
- when TOML::ParseError
113
- "Your key assignments dictionary file (#{dict_file_path}) could not be parsed"
114
- when TOML::ValueOverwriteError
115
- "Command `#{e.key}` is declared more than once"
112
+ when TomlRB::ParseError
113
+ "Your key assignments dictionary file (#{dict_file_path}) could not be parsed:\n#{e.message}"
116
114
  end
117
115
 
118
116
  warn <<-EOS
@@ -132,7 +130,7 @@ Press any key to continue
132
130
  end
133
131
 
134
132
  def save!(mappings)
135
- dict = TOML.dump(mappings).gsub("\n[", "\n\n[")
133
+ dict = TomlRB.dump(mappings).gsub("\n[", "\n\n[")
136
134
  File.open(dict_file_path, 'w', 0644) { |f| f.write(dict) }
137
135
  end
138
136
  end
@@ -35,8 +35,6 @@ module Twterm
35
35
 
36
36
  def translate(key)
37
37
  case key
38
- when '!'..'}' then key
39
- when /\A\^([A-Z]?)\Z/ then $1.ord - 'A'.ord + 1
40
38
  when 'F1' then Curses::Key::F1
41
39
  when 'F2' then Curses::Key::F2
42
40
  when 'F3' then Curses::Key::F3
@@ -49,6 +47,8 @@ module Twterm
49
47
  when 'F10' then Curses::Key::F10
50
48
  when 'F11' then Curses::Key::F11
51
49
  when 'F12' then Curses::Key::F12
50
+ when /\A\^([A-Z]?)\Z/ then $1.ord - 'A'.ord + 1
51
+ when '!'..'}' then key
52
52
  else
53
53
  raise NoSuchKey.new(key)
54
54
  end