twterm 2.8.0 → 2.9.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.
@@ -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
@@ -7,6 +7,7 @@ require 'twterm/image/empty'
7
7
  require 'twterm/image/horizontal_sequential_image'
8
8
  require 'twterm/image/parens'
9
9
  require 'twterm/image/string_image'
10
+ require 'twterm/image/underlined'
10
11
  require 'twterm/image/vertical_sequential_image'
11
12
 
12
13
  class Twterm::Image
@@ -14,6 +15,10 @@ class Twterm::Image
14
15
  @column, @line = column, line
15
16
  end
16
17
 
18
+ def _
19
+ underlined
20
+ end
21
+
17
22
  def !
18
23
  bold
19
24
  end
@@ -101,6 +106,27 @@ class Twterm::Image
101
106
  StringImage.new(str)
102
107
  end
103
108
 
109
+ # @param items [Array<Symbol>]
110
+ # @param selected [Symbol]
111
+ #
112
+ # @return [Image]
113
+ def self.toggle_switch(items, selected)
114
+ items
115
+ .map do |item|
116
+ on = item == selected
117
+ string(item.to_s)
118
+ .bold(on)
119
+ .underlined(on)
120
+ end
121
+ .intersperse(string(' | '))
122
+ .reduce(empty) { |acc, x| acc - x }
123
+ .brackets
124
+ end
125
+
126
+ def underlined(on = true)
127
+ on ? Underlined.new(self) : self
128
+ end
129
+
104
130
  def self.whitespace
105
131
  string(' ')
106
132
  end
@@ -0,0 +1,31 @@
1
+ module Twterm
2
+ class Image
3
+ class Underlined < 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_UNDERLINE)
14
+ image.at(line, column).render(window)
15
+ window.attroff(Curses::A_UNDERLINE)
16
+ end
17
+
18
+ def to_s
19
+ "\e[4m#{image}\e[0m"
20
+ end
21
+
22
+ def width
23
+ image.width
24
+ end
25
+
26
+ private
27
+
28
+ attr_reader :image
29
+ end
30
+ end
31
+ 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
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
@@ -5,11 +5,10 @@ require 'twterm/event/screen/resize'
5
5
  module Twterm
6
6
  class MessageWindow
7
7
  include Singleton
8
- include Curses
9
8
  include Subscriber
10
9
 
11
10
  def initialize
12
- @window = stdscr.subwin(1, stdscr.maxx, stdscr.maxy - 2, 0)
11
+ @window = Curses.stdscr.subwin(1, Curses.stdscr.maxx, Curses.stdscr.maxy - 1, 0)
13
12
  @queue = Queue.new
14
13
 
15
14
  subscribe(Event::Message::AbstractMessage) do |e|
@@ -34,7 +33,7 @@ module Twterm
34
33
 
35
34
  def show(message = nil)
36
35
  loop do
37
- break unless closed?
36
+ break unless Curses.closed?
38
37
  sleep 0.5
39
38
  end
40
39
 
@@ -74,8 +73,8 @@ module Twterm
74
73
  end
75
74
 
76
75
  def resize(_event)
77
- @window.resize(1, stdscr.maxx)
78
- @window.move(stdscr.maxy - 2, 0)
76
+ @window.resize(1, Curses.stdscr.maxx)
77
+ @window.move(Curses.stdscr.maxy - 1, 0)
79
78
  end
80
79
  end
81
80
  end
@@ -1,4 +1,4 @@
1
- require 'toml'
1
+ require 'toml-rb'
2
2
 
3
3
  module Twterm
4
4
  class PersistableConfigurationProxy
@@ -32,16 +32,16 @@ module Twterm
32
32
  # @param [String] filepath File path to load configuration from
33
33
  # @return [Twterm::PersistableConfigurationProxy] a configuration proxy
34
34
  def self.load_from_file!(klass, filepath)
35
- config = TOML.load_file(filepath, symbolize_keys: true)
35
+ config = TomlRB.load_file(filepath, symbolize_keys: true)
36
36
  new(klass.new(config), filepath).migrate!
37
37
  rescue Errno::ENOENT
38
38
  new(klass.default, filepath)
39
- rescue TOML::ParseError, TOML::ValueOverwriteError => e
39
+ rescue TomlRB::ParseError, TomlRB::ValueOverwriteError => e
40
40
  msg =
41
41
  case e
42
- when TOML::ParseError
42
+ when TomlRB::ParseError
43
43
  "Your configuration file could not be parsed"
44
- when TOML::ValueOverwriteError
44
+ when TomlRB::ValueOverwriteError
45
45
  "`#{e.key}` is declared more than once"
46
46
  end
47
47
 
@@ -75,7 +75,7 @@ Press any key to continue
75
75
  attr_reader :filepath, :instance
76
76
 
77
77
  def persist!
78
- hash = TOML.dump(instance.to_h).gsub("\n[", "\n\n[")
78
+ hash = TomlRB.dump(instance.to_h).gsub("\n[", "\n\n[")
79
79
  File.open(filepath, 'w', 0644) { |f| f.write(hash) }
80
80
  end
81
81
  end
@@ -1,4 +1,4 @@
1
- require 'toml'
1
+ require 'toml-rb'
2
2
 
3
3
  require 'twterm/abstract_persistable_configuration'
4
4
 
@@ -27,6 +27,9 @@ module Twterm
27
27
  # @return [Twterm::Preferences] an instance having the default value
28
28
  def self.default
29
29
  new({
30
+ control: {
31
+ scroll_direction: 'traditional',
32
+ },
30
33
  photo_viewer_backend: {
31
34
  browser: true,
32
35
  imgcat: false,
@@ -48,8 +51,12 @@ module Twterm
48
51
  # @return [Hash]
49
52
  def self.structure
50
53
  bool = -> x { x == true || x == false }
54
+ scroll_direction = -> x { x == 'natural' || x == 'traditional' }
51
55
 
52
56
  {
57
+ control: {
58
+ scroll_direction: scroll_direction
59
+ },
53
60
  photo_viewer_backend: {
54
61
  browser: bool,
55
62
  imgcat: bool,
data/lib/twterm/screen.rb CHANGED
@@ -6,18 +6,18 @@ require 'twterm/subscriber'
6
6
  module Twterm
7
7
  class Screen
8
8
  include Subscriber
9
- include Curses
10
9
 
11
10
  def initialize(app, client)
12
11
  @app, @client = app, client
13
12
 
14
- @screen = init_screen
15
- noecho
16
- raw
17
- curs_set(0)
18
- stdscr.keypad(true)
19
- start_color
20
- use_default_colors
13
+ @screen = Curses.init_screen
14
+ Curses.noecho
15
+ Curses.raw
16
+ Curses.curs_set(0)
17
+ Curses.stdscr.keypad(true)
18
+ Curses.start_color
19
+ Curses.use_default_colors
20
+ Curses.mousemask(Curses::BUTTON1_CLICKED | 65536 | 2097152)
21
21
 
22
22
  subscribe(Event::Screen::Refresh) { refresh }
23
23
  subscribe(Event::Screen::Resize, :resize)
@@ -53,6 +53,42 @@ module Twterm
53
53
 
54
54
  attr_reader :app, :client
55
55
 
56
+ # @param [Integer, String] key
57
+ def handle_keyboard_event(key)
58
+ return if app.tab_manager.current_tab.respond_to_key(key)
59
+ return if app.tab_manager.respond_to_key(key)
60
+ respond_to_key(key)
61
+ end
62
+
63
+ # @param [Curses::MouseEvent] e
64
+ def handle_mouse_event(e)
65
+ x = e.x
66
+ y = e.y
67
+
68
+ case e.bstate
69
+ when Curses::BUTTON1_CLICKED
70
+ return app.tab_manager.handle_left_click(x, y) if app.tab_manager.enclose?(x, y)
71
+ when 65536
72
+ scroll_direction = app.preferences[:control, :scroll_direction]
73
+
74
+ case scroll_direction
75
+ when 'natural'
76
+ return app.tab_manager.handle_scroll_up(x, y) if app.tab_manager.enclose?(x, y)
77
+ when 'traditional'
78
+ return app.tab_manager.handle_scroll_down(x, y) if app.tab_manager.enclose?(x, y)
79
+ end
80
+ when 2097152
81
+ scroll_direction = app.preferences[:control, :scroll_direction]
82
+
83
+ case scroll_direction
84
+ when 'natural'
85
+ return app.tab_manager.handle_scroll_down(x, y) if app.tab_manager.enclose?(x, y)
86
+ when 'traditional'
87
+ return app.tab_manager.handle_scroll_up(x, y) if app.tab_manager.enclose?(x, y)
88
+ end
89
+ end
90
+ end
91
+
56
92
  def refresh
57
93
  app.tab_manager.refresh_window
58
94
  app.tab_manager.current_tab.render
@@ -60,10 +96,10 @@ module Twterm
60
96
  end
61
97
 
62
98
  def resize(event)
63
- return if closed?
99
+ return if Curses.closed?
64
100
 
65
101
  lines, cols = event.lines, event.cols
66
- resizeterm(lines, cols)
102
+ Curses.resizeterm(lines, cols)
67
103
  @screen.resize(lines, cols)
68
104
 
69
105
  refresh
@@ -72,11 +108,15 @@ module Twterm
72
108
  def scan
73
109
  app.reset_interruption_handler
74
110
 
75
- key = getch
111
+ key = Curses.getch
76
112
 
77
- return if app.tab_manager.current_tab.respond_to_key(key)
78
- return if app.tab_manager.respond_to_key(key)
79
- respond_to_key(key)
113
+ if key == Curses::Key::MOUSE
114
+ e = Curses.getmouse
115
+
116
+ handle_mouse_event(e) unless e.nil?
117
+ else
118
+ handle_keyboard_event(key)
119
+ end
80
120
  end
81
121
  end
82
122
  end
@@ -3,7 +3,6 @@ require 'twterm/subscriber'
3
3
 
4
4
  module Twterm
5
5
  class SearchQueryWindow
6
- include Curses
7
6
  include Singleton
8
7
  include Subscriber
9
8
 
@@ -12,7 +11,7 @@ module Twterm
12
11
  attr_reader :last_query
13
12
 
14
13
  def initialize
15
- @window = stdscr.subwin(1, stdscr.maxx, stdscr.maxy - 1, 0)
14
+ @window = Curses.stdscr.subwin(1, Curses.stdscr.maxx, Curses.stdscr.maxy - 1, 0)
16
15
  @searching_down = true
17
16
  @str = ''
18
17
  @last_query = ''
@@ -29,7 +28,7 @@ module Twterm
29
28
  chars = []
30
29
 
31
30
  loop do
32
- char = getch
31
+ char = Curses.getch
33
32
 
34
33
  if char.nil?
35
34
  case chars.first
@@ -117,8 +116,8 @@ module Twterm
117
116
  attr_reader :window
118
117
 
119
118
  def resize(_event)
120
- window.resize(1, stdscr.maxx)
121
- window.move(stdscr.maxy - 1, 0)
119
+ window.resize(1, Curses.stdscr.maxx)
120
+ window.move(Curses.stdscr.maxy - 1, 0)
122
121
  end
123
122
 
124
123
  def render(str)
data/lib/twterm/status.rb CHANGED
@@ -12,6 +12,7 @@ class Twitter::Tweet
12
12
  end
13
13
 
14
14
  module Twterm
15
+ # A tweet
15
16
  class Status
16
17
  attr_reader :created_at, :favorite_count, :favorited, :hashtags, :id,
17
18
  :in_reply_to_status_id, :media, :retweet_count, :retweeted,
@@ -23,11 +24,13 @@ module Twterm
23
24
  other.is_a?(self.class) && id == other.id
24
25
  end
25
26
 
27
+ # @todo This should be done in a presenter
26
28
  def date
27
29
  format = Time.now - @created_at < 86_400 ? '%H:%M:%S' : '%Y-%m-%d %H:%M:%S'
28
30
  @created_at.strftime(format)
29
31
  end
30
32
 
33
+ # @todo This can be marked as private
31
34
  def expand_url!
32
35
  sub = -> (x) { @text.sub!(x.url, x.display_url) }
33
36
  (@media + @urls).each(&sub)
@@ -69,10 +72,16 @@ module Twterm
69
72
  expand_url!
70
73
  end
71
74
 
75
+ # Is this status a quote?
76
+ #
77
+ # @return [Boolean]
72
78
  def quote?
73
79
  !quoted_status_id.nil?
74
80
  end
75
81
 
82
+ # Is this status a retweet?
83
+ #
84
+ # @return [Boolean]
76
85
  def retweet?
77
86
  !retweeted_status_id.nil?
78
87
  end
@@ -93,6 +102,7 @@ module Twterm
93
102
  @retweeted = false
94
103
  end
95
104
 
105
+ # @return [self]
96
106
  def update!(tweet, is_retweeted_status = false)
97
107
  @retweet_count = tweet.retweet_count
98
108
  @favorite_count = tweet.favorite_count