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.
@@ -7,20 +7,31 @@ require 'twterm/subscriber'
7
7
  module Twterm
8
8
  module Tab
9
9
  class AbstractTab
10
- include Curses
11
10
  include Subscriber
12
11
 
13
- attr_reader :window, :title
12
+ # @return [String]
13
+ attr_reader :title
14
14
 
15
+ # @return [Curses::Window]
16
+ # @todo This can be (and should be) private
17
+ attr_reader :window
18
+
19
+ # @param other [Twterm::Tab::AbstractTab]
20
+ #
21
+ # @return [Boolean]
15
22
  def ==(other)
16
23
  self.equal?(other)
17
24
  end
18
25
 
26
+ # @return [void]
19
27
  def close
20
28
  unsubscribe
21
29
  window.close
22
30
  end
23
31
 
32
+ # A utility method to find a status by its ID
33
+ #
34
+ # @return [Concurrent::Promise<Twterm::Status>]
24
35
  def find_or_fetch_status(id)
25
36
  status = app.status_repository.find(id)
26
37
 
@@ -31,6 +42,9 @@ module Twterm
31
42
  end
32
43
  end
33
44
 
45
+ # A utility method to find a list by their ID
46
+ #
47
+ # @return [Concurrent::Promise<Twterm::List>]
34
48
  def find_or_fetch_list(id)
35
49
  list = app.list_repository.find(id)
36
50
 
@@ -41,6 +55,9 @@ module Twterm
41
55
  end
42
56
  end
43
57
 
58
+ # A utility method to find a user by their id
59
+ #
60
+ # @return [Concurrent::Promise<Twterm::User>]
44
61
  def find_or_fetch_user(id)
45
62
  user = app.user_repository.find(id)
46
63
 
@@ -54,7 +71,7 @@ module Twterm
54
71
  def initialize(app, client)
55
72
  @app, @client = app, client
56
73
 
57
- @window = stdscr.subwin(stdscr.maxy - 5, stdscr.maxx, 3, 0)
74
+ @window = Curses.stdscr.subwin(Curses.stdscr.maxy - 3, Curses.stdscr.maxx, 2, 0)
58
75
 
59
76
  subscribe(Event::Screen::Resize, :resize)
60
77
  end
@@ -72,7 +89,7 @@ module Twterm
72
89
  end
73
90
  end
74
91
 
75
- view.at(1, 2).render
92
+ view.render
76
93
  end if refreshable?
77
94
  end
78
95
  end
@@ -88,8 +105,13 @@ module Twterm
88
105
 
89
106
  private
90
107
 
91
- attr_reader :app, :client
108
+ # @return [Twterm::App]
109
+ attr_reader :app
110
+
111
+ # @return [Twterm::Client]
112
+ attr_reader :client
92
113
 
114
+ # @return [Twterm::Image]
93
115
  def image
94
116
  Image.string('view method is not implemented')
95
117
  end
@@ -98,19 +120,22 @@ module Twterm
98
120
  @refresh_mutex ||= Mutex.new
99
121
  end
100
122
 
123
+ # @return [Boolean]
101
124
  def refreshable?
102
125
  !(
103
126
  refresh_mutex.locked? ||
104
- closed? ||
127
+ Curses.closed? ||
105
128
  app.tab_manager.current_tab.object_id != object_id
106
129
  )
107
130
  end
108
131
 
132
+ # @return [void]
109
133
  def resize(_event)
110
- window.resize(stdscr.maxy - 5, stdscr.maxx)
111
- window.move(3, 0)
134
+ window.resize(Curses.stdscr.maxy - 3, Curses.stdscr.maxx)
135
+ window.move(2, 0)
112
136
  end
113
137
 
138
+ # @return [Twterm::View]
114
139
  def view
115
140
  View.new(window, image)
116
141
  end
@@ -32,13 +32,13 @@ module Twterm
32
32
 
33
33
  def invoke_input
34
34
  resetter = proc do
35
- reset_prog_mode
35
+ Curses.reset_prog_mode
36
36
  sleep 0.1
37
37
  publish(Event::Screen::Refresh.new)
38
38
  end
39
39
 
40
40
  input_thread = Thread.new do
41
- close_screen
41
+ Curses.close_screen
42
42
  app.completion_manager.set_search_mode!
43
43
  puts "\ninput search query"
44
44
  query = (readline('> ', true) || '').strip
@@ -16,7 +16,7 @@ module Twterm
16
16
 
17
17
  def invoke_input
18
18
  resetter = proc do
19
- reset_prog_mode
19
+ Curses.reset_prog_mode
20
20
  sleep 0.1
21
21
  publish(Event::Screen::Refresh.new)
22
22
  end
@@ -24,7 +24,7 @@ module Twterm
24
24
  app.completion_manager.set_screen_name_mode!
25
25
 
26
26
  input_thread = Thread.new do
27
- close_screen
27
+ Curses.close_screen
28
28
  puts "\nSearch user"
29
29
  screen_name = (readline('> @', true) || '').strip
30
30
  resetter.call
@@ -0,0 +1,77 @@
1
+ require 'twterm/image'
2
+ require 'twterm/preferences'
3
+ require 'twterm/publisher'
4
+ require 'twterm/tab/abstract_tab'
5
+ require 'twterm/tab/scrollable'
6
+
7
+ module Twterm
8
+ module Tab
9
+ module Preferences
10
+ class Control < AbstractTab
11
+ include Scrollable
12
+ include Publisher
13
+
14
+ def drawable_item_count
15
+ 1
16
+ end
17
+
18
+ def image
19
+ drawable_items.map.with_index do |item, i|
20
+ curr = scroller.current_index?(i)
21
+ cursor = Image.cursor(2, curr)
22
+ options = Image.toggle_switch(['traditional', 'natural'], app.preferences[:control, item])
23
+ desc =
24
+ case item
25
+ when :scroll_direction
26
+ header = Image.string('Scroll direction')
27
+ body = Image.string(' ') - options
28
+ cursor - Image.whitespace - (header | body)
29
+ end
30
+ end
31
+ .intersperse(Image.blank_line)
32
+ .reduce(Image.empty) { |acc, x| acc | x }
33
+ end
34
+
35
+ def items
36
+ [
37
+ :scroll_direction,
38
+ ]
39
+ end
40
+
41
+ def respond_to_key(key)
42
+ return true if scroller.respond_to_key(key)
43
+
44
+ case key
45
+ when 10
46
+ perform_selected_action
47
+ end
48
+
49
+ false
50
+ end
51
+
52
+ def title
53
+ 'Control preferences'
54
+ end
55
+
56
+ private
57
+
58
+ def perform_selected_action
59
+ item = scroller.current_item
60
+
61
+ case item
62
+ when :scroll_direction
63
+ app.preferences[:control, :scroll_direction] =
64
+ case app.preferences[:control, :scroll_direction]
65
+ when 'natural'
66
+ 'traditional'
67
+ when 'traditional'
68
+ 'natural'
69
+ end
70
+ end
71
+
72
+ render
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
@@ -1,4 +1,5 @@
1
1
  require 'twterm/image'
2
+ require 'twterm/tab/preferences/control'
2
3
  require 'twterm/tab/preferences/notification_backend'
3
4
  require 'twterm/tab/preferences/photo_viewer_backend'
4
5
  require 'twterm/preferences'
@@ -26,6 +27,8 @@ module Twterm
26
27
  cursor = Image.cursor(1, curr)
27
28
  desc =
28
29
  case item
30
+ when :control
31
+ 'Control preferences'
29
32
  when :notification_backend
30
33
  'Notification backend preferences'
31
34
  when :photo_viewer_backend
@@ -40,6 +43,7 @@ module Twterm
40
43
 
41
44
  def items
42
45
  [
46
+ :control,
43
47
  :notification_backend,
44
48
  :photo_viewer_backend,
45
49
  ]
@@ -67,6 +71,8 @@ module Twterm
67
71
  def open
68
72
  tab =
69
73
  case scroller.current_item
74
+ when :control
75
+ Tab::Preferences::Control.new(app, client)
70
76
  when :notification_backend
71
77
  Tab::Preferences::NotificationBackend.new(app, client)
72
78
  when :photo_viewer_backend
@@ -67,6 +67,7 @@ module Twterm
67
67
 
68
68
  def items
69
69
  [
70
+ :show_conversation,
70
71
  :reply,
71
72
  :favorite,
72
73
  :retweet,
@@ -135,6 +136,8 @@ module Twterm
135
136
  Image.string('Quote this tweet')
136
137
  when :destroy
137
138
  Image.string('Delete this tweet')
139
+ when :show_conversation
140
+ Image.string("Show conversation")
138
141
  when :show_user
139
142
  Image.string("Show user (@#{user.screen_name})")
140
143
  when :open_in_browser
@@ -172,6 +175,8 @@ module Twterm
172
175
  quote!
173
176
  when :destroy
174
177
  destroy!
178
+ when :show_conversation
179
+ show_conversation!
175
180
  when :show_user
176
181
  show_user!
177
182
  when :open_in_browser
@@ -209,6 +214,11 @@ module Twterm
209
214
  .then { render }
210
215
  end
211
216
 
217
+ def show_conversation!
218
+ tab = Tab::Statuses::Conversation.new(app, client, status_id)
219
+ app.tab_manager.add_and_show(tab)
220
+ end
221
+
212
222
  def show_user!
213
223
  user_id = status.user_id
214
224
  user_tab = Tab::UserTab.new(app, client, user_id)
@@ -27,7 +27,7 @@ module Twterm
27
27
  return if @status_ids.include?(status.id)
28
28
 
29
29
  @status_ids.push(status.id)
30
- status.split(window.maxx - 4)
30
+ status.split(window.maxx - 2)
31
31
  scroller.item_appended!
32
32
  render
33
33
  end
@@ -46,7 +46,7 @@ module Twterm
46
46
 
47
47
  def drawable_item_count
48
48
  statuses.drop(scroller.offset).lazy
49
- .map { |s| s.split(window.maxx - 4).count + 2 }
49
+ .map { |s| s.split(window.maxx - 2).count + 2 }
50
50
  .scan(0, :+)
51
51
  .each_cons(2)
52
52
  .select { |_, l| l < window.maxy }
@@ -113,7 +113,7 @@ module Twterm
113
113
  return if @status_ids.include?(status.id)
114
114
 
115
115
  @status_ids.unshift(status.id)
116
- status.split(window.maxx - 4)
116
+ status.split(window.maxx - 2)
117
117
  scroller.item_prepended!
118
118
  render
119
119
  end
@@ -235,7 +235,7 @@ module Twterm
235
235
  ].compact.intersperse(Image.whitespace).reduce(Image.empty, :-)
236
236
 
237
237
  body = original
238
- .split(window.maxx - 4)
238
+ .split(window.maxx - 2)
239
239
  .map(&Image.method(:string))
240
240
  .reduce(Image.empty, :|)
241
241
 
@@ -6,7 +6,6 @@ require 'twterm/view'
6
6
 
7
7
  module Twterm
8
8
  class TabManager
9
- include Curses
10
9
  include Publisher
11
10
  include Subscriber
12
11
  include Utils
@@ -69,6 +68,20 @@ module Twterm
69
68
  end
70
69
  end
71
70
 
71
+ # Returns if the given coordinate is enclosed by the window
72
+ #
73
+ # @param x [Integer]
74
+ # @param y [Integer]
75
+ # @return [Boolean]
76
+ def enclose?(x, y)
77
+ left = @window.begx
78
+ top = @window.begy
79
+ right = left + @window.maxx
80
+ bottom = top + @window.maxy
81
+
82
+ left <= x && x < right && top <= y && y < bottom
83
+ end
84
+
72
85
  def initialize(app, client)
73
86
  @app, @client = app, client
74
87
 
@@ -76,11 +89,50 @@ module Twterm
76
89
  @index = 0
77
90
  @history = []
78
91
 
79
- @window = stdscr.subwin(3, stdscr.maxx, 0, 0)
92
+ @window = Curses.stdscr.subwin(1, Curses.stdscr.maxx, 0, 0)
80
93
 
81
94
  subscribe(Event::Screen::Resize, :resize)
82
95
  end
83
96
 
97
+ # Open the clicked tab
98
+ #
99
+ # @param x [Integer]
100
+ # @param _y [Integer]
101
+ #
102
+ # @return [nil]
103
+ def handle_left_click(x, _y)
104
+ n = find_tab_index_on_x(x)
105
+ return if n.nil?
106
+
107
+ show_nth_tab(n)
108
+
109
+ nil
110
+ end
111
+
112
+ # Open next tab
113
+ #
114
+ # @param _x [Integer]
115
+ # @param _y [Integer]
116
+ #
117
+ # @return [nil]
118
+ def handle_scroll_down(_x, _y)
119
+ show_next
120
+
121
+ nil
122
+ end
123
+
124
+ # Open previous tab
125
+ #
126
+ # @param _x [Integer]
127
+ # @param _y [Integer]
128
+ #
129
+ # @return [nil]
130
+ def handle_scroll_up(_x, _y)
131
+ show_previous
132
+
133
+ nil
134
+ end
135
+
84
136
  def open_my_profile
85
137
  current_user_id = client.user_id
86
138
  tab = Tab::UserTab.new(app, client, current_user_id)
@@ -120,10 +172,10 @@ module Twterm
120
172
 
121
173
  image = @tabs
122
174
  .map { |t| [t, Image.string(t.title)] }
123
- .map { |t, r| t.equal?(current_tab) ? !r : r }
175
+ .map { |t, r| t.equal?(current_tab) ? !r._ : r }
124
176
  .reduce(pipe) { |acc, x| acc - wss - x - wss - pipe }
125
177
 
126
- View.new(@window, image).at(1, 1)
178
+ View.new(@window, image)
127
179
  end
128
180
 
129
181
  def respond_to_key(key)
@@ -195,8 +247,28 @@ module Twterm
195
247
 
196
248
  attr_reader :app, :client
197
249
 
250
+ # @param x [Integer]
251
+ #
252
+ # @return [Integer, nil]
253
+ def find_tab_index_on_x(x)
254
+ pos = 0
255
+
256
+ @tabs.each.with_index do |tab, index|
257
+ title = tab.title
258
+ len = title.length
259
+ left = pos + 1
260
+ right = left + len + 4 # each tab has 2 whitespaces on the both side
261
+
262
+ return index if left <= x && x < right
263
+
264
+ pos = right
265
+ end
266
+
267
+ nil
268
+ end
269
+
198
270
  def resize(_event)
199
- @window.resize(3, stdscr.maxx)
271
+ @window.resize(1, Curses.stdscr.maxx)
200
272
  @window.move(0, 0)
201
273
  end
202
274
  end