twterm 1.0.9 → 1.0.10
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rspec +2 -0
- data/{LICENSE.txt → LICENSE} +0 -0
- data/README.md +24 -2
- data/lib/twterm/app.rb +14 -4
- data/lib/twterm/auth.rb +5 -5
- data/lib/twterm/client.rb +185 -142
- data/lib/twterm/color_manager.rb +15 -11
- data/lib/twterm/config.rb +22 -17
- data/lib/twterm/list.rb +9 -9
- data/lib/twterm/notifier.rb +7 -22
- data/lib/twterm/scheduler.rb +5 -8
- data/lib/twterm/screen.rb +25 -20
- data/lib/twterm/status.rb +84 -79
- data/lib/twterm/tab/base.rb +26 -11
- data/lib/twterm/tab/conversation_tab.rb +16 -16
- data/lib/twterm/tab/key_assignments_cheatsheet.rb +113 -0
- data/lib/twterm/tab/list_tab.rb +1 -1
- data/lib/twterm/tab/mentions_tab.rb +13 -13
- data/lib/twterm/tab/new/list.rb +51 -40
- data/lib/twterm/tab/new/search.rb +8 -5
- data/lib/twterm/tab/new/start.rb +26 -35
- data/lib/twterm/tab/new/user.rb +8 -5
- data/lib/twterm/tab/scroll_manager.rb +84 -0
- data/lib/twterm/tab/search_tab.rb +16 -16
- data/lib/twterm/tab/statuses_tab.rb +177 -128
- data/lib/twterm/tab/timeline_tab.rb +13 -15
- data/lib/twterm/tab/user_tab.rb +18 -18
- data/lib/twterm/tab_manager.rb +49 -44
- data/lib/twterm/tweetbox.rb +51 -25
- data/lib/twterm/user.rb +5 -8
- data/lib/twterm/version.rb +1 -1
- data/lib/twterm.rb +4 -2
- data/spec/resources/config +4 -0
- data/spec/spec_helper.rb +7 -0
- data/spec/twterm/config_spec.rb +34 -0
- data/twterm.gemspec +2 -0
- metadata +25 -6
- data/lib/twterm/tab/scrollable.rb +0 -130
data/lib/twterm/list.rb
CHANGED
@@ -4,6 +4,10 @@ module Twterm
|
|
4
4
|
|
5
5
|
@@instances = {}
|
6
6
|
|
7
|
+
def ==(other)
|
8
|
+
other.is_a?(self.class) && id == other.id
|
9
|
+
end
|
10
|
+
|
7
11
|
def initialize(list)
|
8
12
|
@id = list.id
|
9
13
|
update!(list)
|
@@ -24,13 +28,8 @@ module Twterm
|
|
24
28
|
self
|
25
29
|
end
|
26
30
|
|
27
|
-
def
|
28
|
-
|
29
|
-
end
|
30
|
-
|
31
|
-
def self.new(list)
|
32
|
-
instance = find(list.id)
|
33
|
-
instance.nil? ? super : instance.update!(list)
|
31
|
+
def self.all
|
32
|
+
@@instances.values
|
34
33
|
end
|
35
34
|
|
36
35
|
def self.find(id)
|
@@ -44,8 +43,9 @@ module Twterm
|
|
44
43
|
Client.current.list(id) { |list| yield list }
|
45
44
|
end
|
46
45
|
|
47
|
-
def self.
|
48
|
-
|
46
|
+
def self.new(list)
|
47
|
+
instance = find(list.id)
|
48
|
+
instance.nil? ? super : instance.update!(list)
|
49
49
|
end
|
50
50
|
end
|
51
51
|
end
|
data/lib/twterm/notifier.rb
CHANGED
@@ -4,9 +4,8 @@ module Twterm
|
|
4
4
|
include Curses
|
5
5
|
|
6
6
|
def initialize
|
7
|
-
@window = stdscr.subwin(
|
7
|
+
@window = stdscr.subwin(1, stdscr.maxx, stdscr.maxy - 2, 0)
|
8
8
|
@queue = Queue.new
|
9
|
-
@help = ''
|
10
9
|
|
11
10
|
Thread.new do
|
12
11
|
while notification = @queue.pop
|
@@ -17,23 +16,16 @@ module Twterm
|
|
17
16
|
end
|
18
17
|
end
|
19
18
|
|
20
|
-
def show_message(message)
|
21
|
-
notification = Notification::Message.new(message)
|
22
|
-
@queue.push(notification)
|
23
|
-
self
|
24
|
-
end
|
25
|
-
|
26
19
|
def show_error(message)
|
27
20
|
notification = Notification::Error.new(message)
|
28
21
|
@queue.push(notification)
|
29
22
|
self
|
30
23
|
end
|
31
24
|
|
32
|
-
def
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
show
|
25
|
+
def show_message(message)
|
26
|
+
notification = Notification::Message.new(message)
|
27
|
+
@queue.push(notification)
|
28
|
+
self
|
37
29
|
end
|
38
30
|
|
39
31
|
def show(notification = nil)
|
@@ -46,22 +38,15 @@ module Twterm
|
|
46
38
|
|
47
39
|
if notification.is_a? Notification::Base
|
48
40
|
@window.with_color(notification.fg_color, notification.bg_color) do
|
49
|
-
@window.setpos(
|
41
|
+
@window.setpos(0, 0)
|
50
42
|
@window.addstr(' ' * @window.maxx)
|
51
|
-
@window.setpos(
|
43
|
+
@window.setpos(0, 1)
|
52
44
|
time = notification.time.strftime('[%H:%M:%S]')
|
53
45
|
message = notification.show_with_width(@window.maxx)
|
54
46
|
@window.addstr("#{time} #{message}")
|
55
47
|
end
|
56
48
|
end
|
57
49
|
|
58
|
-
@window.with_color(:black, :green) do
|
59
|
-
@window.setpos(0, 0)
|
60
|
-
@window.addstr(' ' * @window.maxx)
|
61
|
-
@window.setpos(0, 1)
|
62
|
-
@window.addstr(@help)
|
63
|
-
end
|
64
|
-
|
65
50
|
@window.refresh
|
66
51
|
end
|
67
52
|
end
|
data/lib/twterm/scheduler.rb
CHANGED
@@ -7,10 +7,7 @@ class Scheduler
|
|
7
7
|
@paused = false
|
8
8
|
|
9
9
|
@thread = Thread.new do
|
10
|
-
loop
|
11
|
-
sleep @interval
|
12
|
-
run
|
13
|
-
end
|
10
|
+
loop { sleep(@interval) && run }
|
14
11
|
end
|
15
12
|
end
|
16
13
|
|
@@ -22,11 +19,11 @@ class Scheduler
|
|
22
19
|
@paused = true
|
23
20
|
end
|
24
21
|
|
25
|
-
def
|
26
|
-
@
|
22
|
+
def resume
|
23
|
+
@paused = false
|
27
24
|
end
|
28
25
|
|
29
|
-
def
|
30
|
-
@
|
26
|
+
def run
|
27
|
+
@block.call unless @paused
|
31
28
|
end
|
32
29
|
end
|
data/lib/twterm/screen.rb
CHANGED
@@ -13,15 +13,6 @@ module Twterm
|
|
13
13
|
use_default_colors
|
14
14
|
end
|
15
15
|
|
16
|
-
def wait
|
17
|
-
@thread = Thread.new do
|
18
|
-
loop do
|
19
|
-
scan
|
20
|
-
end
|
21
|
-
end
|
22
|
-
@thread.join
|
23
|
-
end
|
24
|
-
|
25
16
|
def refresh
|
26
17
|
TabManager.instance.refresh_window
|
27
18
|
TabManager.instance.current_tab.refresh
|
@@ -29,6 +20,30 @@ module Twterm
|
|
29
20
|
Notifier.instance.show
|
30
21
|
end
|
31
22
|
|
23
|
+
def respond_to_key(key)
|
24
|
+
case key
|
25
|
+
when ?n
|
26
|
+
Tweetbox.instance.compose
|
27
|
+
return
|
28
|
+
when ?Q
|
29
|
+
App.instance.quit
|
30
|
+
when ??
|
31
|
+
tab = Tab::KeyAssignmentsCheatsheet.new
|
32
|
+
TabManager.instance.add_and_show tab
|
33
|
+
else
|
34
|
+
return false
|
35
|
+
end
|
36
|
+
|
37
|
+
true
|
38
|
+
end
|
39
|
+
|
40
|
+
def wait
|
41
|
+
@thread = Thread.new do
|
42
|
+
loop { scan }
|
43
|
+
end
|
44
|
+
@thread.join
|
45
|
+
end
|
46
|
+
|
32
47
|
private
|
33
48
|
|
34
49
|
def scan
|
@@ -38,17 +53,7 @@ module Twterm
|
|
38
53
|
|
39
54
|
return if TabManager.instance.current_tab.respond_to_key(key)
|
40
55
|
return if TabManager.instance.respond_to_key(key)
|
41
|
-
|
42
|
-
case key
|
43
|
-
when 'n'
|
44
|
-
Tweetbox.instance.compose
|
45
|
-
return
|
46
|
-
when 'Q'
|
47
|
-
App.instance.quit
|
48
|
-
when '/'
|
49
|
-
# filter
|
50
|
-
else
|
51
|
-
end
|
56
|
+
respond_to_key(key)
|
52
57
|
end
|
53
58
|
end
|
54
59
|
end
|
data/lib/twterm/status.rb
CHANGED
@@ -2,21 +2,57 @@ module Twterm
|
|
2
2
|
class Status
|
3
3
|
MAX_CACHED_TIME = 3600
|
4
4
|
|
5
|
-
attr_reader :
|
5
|
+
attr_reader :appeared_at, :created_at, :favorite_count, :favorited, :id,
|
6
|
+
:in_reply_to_status_id, :media, :retweet_count, :retweeted,
|
7
|
+
:retweeted_by_user_id, :text, :touched_at, :urls, :user_id
|
6
8
|
alias_method :favorited?, :favorited
|
7
9
|
alias_method :retweeted?, :retweeted
|
8
10
|
|
9
11
|
@@instances = {}
|
10
12
|
|
11
|
-
def
|
12
|
-
|
13
|
-
|
13
|
+
def ==(other)
|
14
|
+
other.is_a?(self.class) && id == other.id
|
15
|
+
end
|
16
|
+
|
17
|
+
def date
|
18
|
+
format = Time.now - @created_at < 86_400 ? '%H:%M:%S' : '%Y-%m-%d %H:%M:%S'
|
19
|
+
@created_at.strftime(format)
|
20
|
+
end
|
21
|
+
|
22
|
+
def expand_url!
|
23
|
+
sub = -> (x) { @text.sub!(x.url, x.display_url) }
|
24
|
+
(@media + @urls).each(&sub)
|
25
|
+
end
|
26
|
+
|
27
|
+
def favorite!
|
28
|
+
@favorite_count += 1
|
29
|
+
@favorited = true
|
30
|
+
end
|
31
|
+
|
32
|
+
def grepped_with?(query)
|
33
|
+
[text, user.screen_name, user.name]
|
34
|
+
.any? { |x| x.downcase.include?(query.downcase) }
|
35
|
+
end
|
36
|
+
|
37
|
+
def in_reply_to_status(&block)
|
38
|
+
if @in_reply_to_status_id.nil?
|
39
|
+
block.call(nil)
|
40
|
+
return
|
41
|
+
end
|
42
|
+
|
43
|
+
status = Status.find(@in_reply_to_status_id)
|
44
|
+
unless status.nil?
|
45
|
+
block.call(status)
|
46
|
+
return
|
47
|
+
end
|
48
|
+
|
49
|
+
Client.current.show_status(@in_reply_to_status_id, &block)
|
14
50
|
end
|
15
51
|
|
16
52
|
def initialize(tweet)
|
17
53
|
unless tweet.retweeted_status.is_a? Twitter::NullObject
|
18
54
|
@retweeted_by_user_id = tweet.user.id
|
19
|
-
User.
|
55
|
+
User.new(tweet.user)
|
20
56
|
retweeted_at = Status.parse_time(tweet.created_at)
|
21
57
|
tweet = tweet.retweeted_status
|
22
58
|
end
|
@@ -24,7 +60,7 @@ module Twterm
|
|
24
60
|
@id = tweet.id
|
25
61
|
@text = CGI.unescapeHTML(tweet.full_text.dup)
|
26
62
|
@created_at = Status.parse_time(tweet.created_at)
|
27
|
-
@
|
63
|
+
@appeared_at = retweeted_at || @created_at
|
28
64
|
@retweet_count = tweet.retweet_count
|
29
65
|
@favorite_count = tweet.favorite_count
|
30
66
|
@in_reply_to_status_id = tweet.in_reply_to_status_id
|
@@ -36,7 +72,7 @@ module Twterm
|
|
36
72
|
@urls = tweet.urls
|
37
73
|
|
38
74
|
@user_id = tweet.user.id
|
39
|
-
User.
|
75
|
+
User.new(tweet.user)
|
40
76
|
|
41
77
|
@splitted_text = {}
|
42
78
|
|
@@ -51,32 +87,8 @@ module Twterm
|
|
51
87
|
@@instances[id] = self
|
52
88
|
end
|
53
89
|
|
54
|
-
def
|
55
|
-
|
56
|
-
@favorite_count = tweet.favorite_count
|
57
|
-
@retweeted = tweet.retweeted?
|
58
|
-
@favorited = tweet.favorited?
|
59
|
-
self
|
60
|
-
end
|
61
|
-
|
62
|
-
def date
|
63
|
-
format = Time.now - @created_at < 86_400 ? '%H:%M:%S' : '%Y-%m-%d %H:%M:%S'
|
64
|
-
@created_at.strftime(format)
|
65
|
-
end
|
66
|
-
|
67
|
-
def expand_url!
|
68
|
-
sub = -> (x) { @text.sub!(x.url, x.display_url) }
|
69
|
-
(@media + @urls).each(&sub)
|
70
|
-
end
|
71
|
-
|
72
|
-
def favorite!
|
73
|
-
@favorite_count += 1
|
74
|
-
@favorited = true
|
75
|
-
end
|
76
|
-
|
77
|
-
def unfavorite!
|
78
|
-
@favorite_count -= 1
|
79
|
-
@favorited = false
|
90
|
+
def replies
|
91
|
+
Status.all.select { |s| s.in_reply_to_status_id == id }
|
80
92
|
end
|
81
93
|
|
82
94
|
def retweet!
|
@@ -84,74 +96,67 @@ module Twterm
|
|
84
96
|
@retweeted = true
|
85
97
|
end
|
86
98
|
|
87
|
-
def
|
88
|
-
|
99
|
+
def retweeted_by
|
100
|
+
User.find(@retweeted_by_user_id)
|
89
101
|
end
|
90
102
|
|
91
|
-
def
|
92
|
-
|
93
|
-
block.call(nil)
|
94
|
-
return
|
95
|
-
end
|
96
|
-
|
97
|
-
status = Status.find(@in_reply_to_status_id)
|
98
|
-
unless status.nil?
|
99
|
-
block.call(status)
|
100
|
-
return
|
101
|
-
end
|
102
|
-
|
103
|
-
Client.current.show_status(@in_reply_to_status_id, &block)
|
103
|
+
def split(width)
|
104
|
+
@splitted_text[:width] ||= @text.split_by_width(width)
|
104
105
|
end
|
105
106
|
|
106
|
-
def
|
107
|
-
|
107
|
+
def touch!
|
108
|
+
@touched_at = Time.now
|
108
109
|
end
|
109
110
|
|
110
|
-
def
|
111
|
-
|
111
|
+
def unfavorite!
|
112
|
+
@favorite_count -= 1
|
113
|
+
@favorited = false
|
112
114
|
end
|
113
115
|
|
114
|
-
def
|
115
|
-
@
|
116
|
+
def update!(tweet)
|
117
|
+
@retweet_count = tweet.retweet_count
|
118
|
+
@favorite_count = tweet.favorite_count
|
119
|
+
@retweeted = tweet.retweeted?
|
120
|
+
@favorited = tweet.favorited?
|
121
|
+
self
|
116
122
|
end
|
117
123
|
|
118
124
|
def user
|
119
125
|
User.find(user_id)
|
120
126
|
end
|
121
127
|
|
122
|
-
def
|
123
|
-
|
128
|
+
def self.all
|
129
|
+
@@instances.values
|
124
130
|
end
|
125
131
|
|
126
|
-
|
127
|
-
|
128
|
-
|
132
|
+
def self.cleanup
|
133
|
+
TabManager.instance.each_tab do |tab|
|
134
|
+
tab.touch_statuses if tab.is_a?(Tab::StatusesTab)
|
129
135
|
end
|
136
|
+
cond = -> (status) { status.touched_at > Time.now - MAX_CACHED_TIME }
|
137
|
+
statuses = all.select(&cond)
|
138
|
+
status_ids = statuses.map(&:id)
|
139
|
+
@@instances = status_ids.zip(statuses).to_h
|
140
|
+
end
|
130
141
|
|
131
|
-
|
132
|
-
|
133
|
-
|
142
|
+
def self.find(id)
|
143
|
+
@@instances[id]
|
144
|
+
end
|
134
145
|
|
135
|
-
|
136
|
-
|
137
|
-
|
146
|
+
def self.find_or_fetch(id)
|
147
|
+
instance = find(id)
|
148
|
+
(yield(instance) && return) if instance
|
138
149
|
|
139
|
-
|
140
|
-
|
150
|
+
Client.current.show_status(id) { |status| yield status }
|
151
|
+
end
|
141
152
|
|
142
|
-
|
143
|
-
|
144
|
-
|
153
|
+
def self.new(tweet)
|
154
|
+
instance = find(tweet.id)
|
155
|
+
instance.nil? ? super : instance.update!(tweet)
|
156
|
+
end
|
145
157
|
|
146
|
-
|
147
|
-
|
148
|
-
tab.touch_statuses if tab.is_a?(Tab::StatusesTab)
|
149
|
-
end
|
150
|
-
cond = -> (status) { status.touched_at > Time.now - MAX_CACHED_TIME }
|
151
|
-
statuses = all.select(&cond)
|
152
|
-
status_ids = statuses.map(&:id)
|
153
|
-
@@instances = status_ids.zip(statuses).to_h
|
154
|
-
end
|
158
|
+
def self.parse_time(time)
|
159
|
+
(time.is_a?(String) ? Time.parse(time) : time.dup).localtime
|
155
160
|
end
|
156
161
|
end
|
157
162
|
end
|
data/lib/twterm/tab/base.rb
CHANGED
@@ -3,35 +3,50 @@ module Twterm
|
|
3
3
|
module Base
|
4
4
|
include Curses
|
5
5
|
|
6
|
+
attr_reader :window
|
6
7
|
attr_accessor :title
|
7
8
|
|
9
|
+
def ==(other)
|
10
|
+
self.equal?(other)
|
11
|
+
end
|
12
|
+
|
13
|
+
def close
|
14
|
+
window.close
|
15
|
+
end
|
16
|
+
|
8
17
|
def initialize
|
9
18
|
@window = stdscr.subwin(stdscr.maxy - 5, stdscr.maxx - 30, 3, 0)
|
10
19
|
end
|
11
20
|
|
12
21
|
def refresh
|
13
|
-
return
|
22
|
+
return unless refreshable?
|
14
23
|
|
15
|
-
@refreshing = true
|
16
24
|
Thread.new do
|
17
|
-
|
18
|
-
|
25
|
+
refresh_mutex.synchronize do
|
26
|
+
window.clear
|
27
|
+
update
|
28
|
+
window.refresh
|
29
|
+
end
|
19
30
|
end
|
20
31
|
end
|
21
32
|
|
22
|
-
def close
|
23
|
-
@window.close
|
24
|
-
end
|
25
|
-
|
26
33
|
def respond_to_key(_)
|
27
34
|
fail NotImplementedError, 'respond_to_key method must be implemented'
|
28
35
|
end
|
29
36
|
|
30
|
-
|
31
|
-
|
37
|
+
private
|
38
|
+
|
39
|
+
def refresh_mutex
|
40
|
+
@refresh_mutex ||= Mutex.new
|
32
41
|
end
|
33
42
|
|
34
|
-
|
43
|
+
def refreshable?
|
44
|
+
!(
|
45
|
+
refresh_mutex.locked? ||
|
46
|
+
closed? ||
|
47
|
+
TabManager.instance.current_tab.object_id != object_id
|
48
|
+
)
|
49
|
+
end
|
35
50
|
|
36
51
|
def update
|
37
52
|
fail NotImplementedError, 'update method must be implemented'
|
@@ -6,18 +6,8 @@ module Twterm
|
|
6
6
|
|
7
7
|
attr_reader :status
|
8
8
|
|
9
|
-
def
|
10
|
-
|
11
|
-
super()
|
12
|
-
|
13
|
-
Status.find_or_fetch(status_id) do |status|
|
14
|
-
@status = status
|
15
|
-
|
16
|
-
append(status)
|
17
|
-
move_to_top
|
18
|
-
Thread.new { fetch_in_reply_to_status(status) }
|
19
|
-
Thread.new { fetch_replies(status) }
|
20
|
-
end
|
9
|
+
def ==(other)
|
10
|
+
other.is_a?(self.class) && status == other.status
|
21
11
|
end
|
22
12
|
|
23
13
|
def fetch_in_reply_to_status(status)
|
@@ -37,13 +27,23 @@ module Twterm
|
|
37
27
|
end
|
38
28
|
end
|
39
29
|
|
40
|
-
def ==(other)
|
41
|
-
other.is_a?(self.class) && status == other.status
|
42
|
-
end
|
43
|
-
|
44
30
|
def dump
|
45
31
|
@status.id
|
46
32
|
end
|
33
|
+
|
34
|
+
def initialize(status_id)
|
35
|
+
@title = 'Conversation'
|
36
|
+
super()
|
37
|
+
|
38
|
+
Status.find_or_fetch(status_id) do |status|
|
39
|
+
@status = status
|
40
|
+
|
41
|
+
append(status)
|
42
|
+
scroll_manager.move_to_top
|
43
|
+
Thread.new { fetch_in_reply_to_status(status) }
|
44
|
+
Thread.new { fetch_replies(status) }
|
45
|
+
end
|
46
|
+
end
|
47
47
|
end
|
48
48
|
end
|
49
49
|
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
module Twterm
|
2
|
+
module Tab
|
3
|
+
class KeyAssignmentsCheatsheet
|
4
|
+
include Base
|
5
|
+
|
6
|
+
def ==(other)
|
7
|
+
other.is_a?(self.class)
|
8
|
+
end
|
9
|
+
|
10
|
+
SHORTCUTS = {
|
11
|
+
'General' => {
|
12
|
+
'[d] [C-d]' => 'Scroll down',
|
13
|
+
'[g]' => 'Move to top',
|
14
|
+
'[G]' => 'Move to bottom',
|
15
|
+
'[j] [C-p] [DOWN]' => 'Move down',
|
16
|
+
'[k] [C-n] [UP]' => 'Move up',
|
17
|
+
'[u] [C-u]' => 'Scroll up',
|
18
|
+
'[Q]' => 'Quit twterm',
|
19
|
+
'[?]' => 'Open key assignments cheatsheet'
|
20
|
+
},
|
21
|
+
'Tabs' => {
|
22
|
+
'[h] [C-b] [LEFT]' => 'Show previous tab',
|
23
|
+
'[l] [C-f] [RIGHT]' => 'Show next tab',
|
24
|
+
'[N]' => 'Open new tab',
|
25
|
+
'[C-R]' => 'Reload',
|
26
|
+
'[w]' => 'Close tab',
|
27
|
+
'[q]' => 'Quit filtering mode',
|
28
|
+
'[/]' => 'Filter items in tab'
|
29
|
+
},
|
30
|
+
'Tweets' => {
|
31
|
+
'[D]' => 'Delete tweet',
|
32
|
+
'[F]' => 'Add to favorite',
|
33
|
+
'[n]' => 'Compose new tweet',
|
34
|
+
'[o]' => 'Open URLs in tweet',
|
35
|
+
'[r]' => 'Reply',
|
36
|
+
'[R]' => 'Retweet',
|
37
|
+
'[U]' => 'Show user'
|
38
|
+
}
|
39
|
+
}
|
40
|
+
|
41
|
+
def respond_to_key(key)
|
42
|
+
case key
|
43
|
+
when ?d, 4
|
44
|
+
10.times { scroll_manager.move_down }
|
45
|
+
when ?g
|
46
|
+
scroll_manager.move_to_top
|
47
|
+
when ?G
|
48
|
+
scroll_manager.move_to_bottom
|
49
|
+
when ?j, 14, Curses::Key::DOWN
|
50
|
+
scroll_manager.move_down
|
51
|
+
when ?k, 16, Curses::Key::UP
|
52
|
+
scroll_manager.move_up
|
53
|
+
when ?u, 21
|
54
|
+
10.times { scroll_manager.move_up }
|
55
|
+
else
|
56
|
+
return false
|
57
|
+
end
|
58
|
+
|
59
|
+
true
|
60
|
+
end
|
61
|
+
|
62
|
+
def title
|
63
|
+
'Key assignments'.freeze
|
64
|
+
end
|
65
|
+
|
66
|
+
def update
|
67
|
+
top = 2 # begin drawing from line 2
|
68
|
+
draw_cond = -> line { top <= line && line <= window.maxy - top }
|
69
|
+
|
70
|
+
current_line = top - scroll_manager.offset
|
71
|
+
|
72
|
+
window.setpos(current_line, 3)
|
73
|
+
window.bold { window.addstr('Key assignments') } if draw_cond[current_line]
|
74
|
+
|
75
|
+
SHORTCUTS.each do |category, shortcuts|
|
76
|
+
current_line += 3
|
77
|
+
window.setpos(current_line, 5)
|
78
|
+
window.bold { window.addstr("<#{category}>") } if draw_cond[current_line]
|
79
|
+
current_line += 1
|
80
|
+
|
81
|
+
shortcuts.each do |key, description|
|
82
|
+
current_line += 1
|
83
|
+
next unless draw_cond[current_line]
|
84
|
+
|
85
|
+
window.setpos(current_line, 7)
|
86
|
+
window.bold { window.addstr(key.rjust(17)) }
|
87
|
+
window.setpos(current_line, 25)
|
88
|
+
window.addstr(": #{description}")
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
private
|
94
|
+
|
95
|
+
def count
|
96
|
+
@count ||= SHORTCUTS.count * 4 + SHORTCUTS.map(&:count).reduce(0, :+) + 1
|
97
|
+
end
|
98
|
+
|
99
|
+
def offset_from_bottom
|
100
|
+
0
|
101
|
+
end
|
102
|
+
|
103
|
+
def scroll_manager
|
104
|
+
return @scroll_manager unless @scroll_manager.nil?
|
105
|
+
|
106
|
+
@scroll_manager = ScrollManager.new
|
107
|
+
@scroll_manager.delegate = self
|
108
|
+
@scroll_manager.after_move { refresh }
|
109
|
+
@scroll_manager
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
data/lib/twterm/tab/list_tab.rb
CHANGED
@@ -3,6 +3,18 @@ module Twterm
|
|
3
3
|
class MentionsTab
|
4
4
|
include StatusesTab
|
5
5
|
|
6
|
+
def close
|
7
|
+
fail NotClosableError
|
8
|
+
end
|
9
|
+
|
10
|
+
def fetch
|
11
|
+
@client.mentions do |statuses|
|
12
|
+
statuses.reverse.each(&method(:prepend))
|
13
|
+
sort
|
14
|
+
yield if block_given?
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
6
18
|
def initialize(client)
|
7
19
|
fail ArgumentError, 'argument must be an instance of Client class' unless client.is_a? Client
|
8
20
|
|
@@ -16,21 +28,9 @@ module Twterm
|
|
16
28
|
|
17
29
|
@title = 'Mentions'
|
18
30
|
|
19
|
-
fetch { move_to_top }
|
31
|
+
fetch { scroll_manager.move_to_top }
|
20
32
|
@auto_reloader = Scheduler.new(300) { fetch }
|
21
33
|
end
|
22
|
-
|
23
|
-
def fetch
|
24
|
-
@client.mentions do |statuses|
|
25
|
-
statuses.reverse.each(&method(:prepend))
|
26
|
-
sort
|
27
|
-
yield if block_given?
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
def close
|
32
|
-
fail NotClosableError
|
33
|
-
end
|
34
34
|
end
|
35
35
|
end
|
36
36
|
end
|