twterm 2.6.0 → 2.10.1

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 (64) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +62 -0
  3. data/.gitignore +1 -0
  4. data/Gemfile +7 -0
  5. data/Makefile +21 -0
  6. data/README.md +52 -6
  7. data/bin/twterm +1 -3
  8. data/default.nix +21 -0
  9. data/gemset.nix +5130 -0
  10. data/lib/twterm/app.rb +13 -24
  11. data/lib/twterm/client.rb +1 -4
  12. data/lib/twterm/color_manager.rb +8 -9
  13. data/lib/twterm/image.rb +31 -0
  14. data/lib/twterm/image/attr.rb +42 -0
  15. data/lib/twterm/image/bold.rb +7 -21
  16. data/lib/twterm/image/color.rb +9 -15
  17. data/lib/twterm/image/dim.rb +13 -0
  18. data/lib/twterm/image/underlined.rb +17 -0
  19. data/lib/twterm/image_builder/user_name_image_builder.rb +1 -0
  20. data/lib/twterm/key_mapper.rb +6 -8
  21. data/lib/twterm/key_mapper/abstract_key_mapper.rb +2 -2
  22. data/lib/twterm/list.rb +36 -1
  23. data/lib/twterm/message_window.rb +4 -13
  24. data/lib/twterm/persistable_configuration_proxy.rb +6 -6
  25. data/lib/twterm/preferences.rb +8 -1
  26. data/lib/twterm/rest_client.rb +0 -33
  27. data/lib/twterm/screen.rb +103 -25
  28. data/lib/twterm/search_query_window.rb +5 -13
  29. data/lib/twterm/status.rb +10 -0
  30. data/lib/twterm/tab/abstract_tab.rb +30 -16
  31. data/lib/twterm/tab/new/index.rb +0 -10
  32. data/lib/twterm/tab/new/search.rb +2 -2
  33. data/lib/twterm/tab/new/user.rb +2 -2
  34. data/lib/twterm/tab/preferences/control.rb +77 -0
  35. data/lib/twterm/tab/preferences/index.rb +6 -0
  36. data/lib/twterm/tab/scrollable.rb +1 -1
  37. data/lib/twterm/tab/searchable.rb +6 -4
  38. data/lib/twterm/tab/status_tab.rb +10 -0
  39. data/lib/twterm/tab/statuses/abstract_statuses_tab.rb +11 -6
  40. data/lib/twterm/tab/user_tab.rb +0 -9
  41. data/lib/twterm/tab_manager.rb +77 -10
  42. data/lib/twterm/tweetbox.rb +2 -3
  43. data/lib/twterm/user.rb +1 -0
  44. data/lib/twterm/version.rb +1 -1
  45. data/nix/Gemfile +3 -0
  46. data/nix/Gemfile.lock +77 -0
  47. data/nix/gemset.nix +325 -0
  48. data/shell.nix +40 -0
  49. data/spec/twterm/image/bold_spec.rb +30 -0
  50. data/spec/twterm/image/color_spec.rb +16 -0
  51. data/spec/twterm/image/dim_spec.rb +30 -0
  52. data/twterm.gemspec +7 -14
  53. metadata +31 -113
  54. data/.travis.yml +0 -12
  55. data/lib/twterm/direct_message.rb +0 -60
  56. data/lib/twterm/direct_message_composer.rb +0 -80
  57. data/lib/twterm/direct_message_manager.rb +0 -51
  58. data/lib/twterm/event/direct_message/fetched.rb +0 -10
  59. data/lib/twterm/event/notification/direct_message.rb +0 -30
  60. data/lib/twterm/event/screen/resize.rb +0 -13
  61. data/lib/twterm/repository/direct_message_repository.rb +0 -14
  62. data/lib/twterm/tab/direct_message/conversation.rb +0 -104
  63. data/lib/twterm/tab/direct_message/conversation_list.rb +0 -100
  64. 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)
@@ -103,11 +98,6 @@ module Twterm
103
98
  user_repository.expire(3600)
104
99
  end
105
100
 
106
- direct_message_repository.before_create do |dm|
107
- user_repository.create(dm.recipient)
108
- user_repository.create(dm.sender)
109
- end
110
-
111
101
  user_repository.before_create do |user|
112
102
  client_id = client.user_id
113
103
 
@@ -158,7 +148,7 @@ module Twterm
158
148
  end
159
149
 
160
150
  def tab_manager
161
- @tab_manager ||= TabManager.new(self, client)
151
+ @tab_manager ||= TabManager.new(self, client, screen.tab_manager_window)
162
152
  end
163
153
 
164
154
  def tweetbox
@@ -179,7 +169,6 @@ module Twterm
179
169
  config[:access_token_secret],
180
170
  {
181
171
  friendship: friendship_repository,
182
- direct_message: direct_message_repository,
183
172
  hashtag: hashtag_repository,
184
173
  list: list_repository,
185
174
  status: status_repository,
@@ -193,11 +182,11 @@ module Twterm
193
182
  end
194
183
 
195
184
  def on_resize
196
- return if Curses.closed?
185
+ lines, cols = `stty size`.split(' ').map(&:to_i)
186
+
187
+ Readline.set_screen_size(lines, cols)
197
188
 
198
- lines = `tput lines`.to_i
199
- cols = `tput cols`.to_i
200
- publish(Event::Screen::Resize.new(lines, cols))
189
+ screen.resize(lines, cols) unless Curses.closed?
201
190
  end
202
191
  end
203
192
  end
data/lib/twterm/client.rb CHANGED
@@ -11,7 +11,6 @@ module Twterm
11
11
  @access_token, @access_token_secret = access_token, access_token_secret
12
12
 
13
13
  @friendship_repository = repositories[:friendship]
14
- @direct_message_repository = repositories[:direct_message]
15
14
  @hashtag_repository = repositories[:hashtag]
16
15
  @list_repository = repositories[:list]
17
16
  @status_repository = repositories[:status]
@@ -27,12 +26,10 @@ module Twterm
27
26
  muted_user_ids.include?(status.retweeted_status.user.id))
28
27
  end
29
28
  end
30
-
31
- direct_message_manager
32
29
  end
33
30
 
34
31
  private
35
32
 
36
- 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
37
34
  end
38
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
data/lib/twterm/list.rb CHANGED
@@ -1,7 +1,41 @@
1
1
  module Twterm
2
+ # A Twitter list
2
3
  class List
3
- attr_reader :id, :name, :slug, :full_name, :mode, :description, :member_count, :subscriber_count, :url
4
+ # Unique ID of the list
5
+ #
6
+ # @return [Integer]
7
+ attr_reader :id
4
8
 
9
+ # @return [String]
10
+ attr_reader :name
11
+
12
+ # @return [String]
13
+ attr_reader :slug
14
+
15
+ # @return [String]
16
+ attr_reader :full_name
17
+
18
+ attr_reader :mode
19
+
20
+ # @return [String]
21
+ attr_reader :description
22
+
23
+ # The number of users that are in this list
24
+ #
25
+ # @return [Integer]
26
+ attr_reader :member_count
27
+
28
+ # The number of users that subscribe this list
29
+ #
30
+ # @return [Integer]
31
+ attr_reader :subscriber_count
32
+
33
+ # @return [String]
34
+ attr_reader :url
35
+
36
+ # @param other [List]
37
+ #
38
+ # @return [Boolean]
5
39
  def ==(other)
6
40
  other.is_a?(self.class) && id == other.id
7
41
  end
@@ -11,6 +45,7 @@ module Twterm
11
45
  update!(list)
12
46
  end
13
47
 
48
+ # @return [self]
14
49
  def update!(list)
15
50
  @name = list.name
16
51
  @slug = list.slug