twterm 2.8.0 → 2.9.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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