twterm 1.3.0 → 2.0.0.beta1
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.
- checksums.yaml +4 -4
- data/README.md +14 -18
- data/bin/twterm +1 -1
- data/lib/twterm/app.rb +120 -30
- data/lib/twterm/client.rb +10 -13
- data/lib/twterm/completion_mamanger.rb +11 -6
- data/lib/twterm/direct_message.rb +6 -28
- data/lib/twterm/direct_message_composer.rb +10 -5
- data/lib/twterm/direct_message_manager.rb +5 -6
- data/lib/twterm/event/notification/abstract_notification.rb +27 -0
- data/lib/twterm/event/notification/error.rb +13 -0
- data/lib/twterm/event/notification/info.rb +13 -0
- data/lib/twterm/event/notification/success.rb +13 -0
- data/lib/twterm/event/notification/warning.rb +13 -0
- data/lib/twterm/event_dispatcher.rb +1 -1
- data/lib/twterm/extensions/array.rb +5 -0
- data/lib/twterm/extensions/enumerator/lazy.rb +3 -0
- data/lib/twterm/extensions/string.rb +0 -4
- data/lib/twterm/friendship.rb +1 -85
- data/lib/twterm/image/between.rb +31 -0
- data/lib/twterm/image/blank_line.rb +21 -0
- data/lib/twterm/image/bold.rb +31 -0
- data/lib/twterm/image/brackets.rb +21 -0
- data/lib/twterm/image/color.rb +45 -0
- data/lib/twterm/image/empty.rb +21 -0
- data/lib/twterm/image/horizontal_sequential_image.rb +48 -0
- data/lib/twterm/image/parens.rb +21 -0
- data/lib/twterm/image/string_image.rb +38 -0
- data/lib/twterm/image/vertical_sequential_image.rb +43 -0
- data/lib/twterm/image.rb +107 -0
- data/lib/twterm/key_mapper/abstract_key_mapper.rb +51 -0
- data/lib/twterm/key_mapper/app_key_mapper.rb +13 -0
- data/lib/twterm/key_mapper/cursor_key_mapper.rb +13 -0
- data/lib/twterm/key_mapper/general_key_mapper.rb +18 -0
- data/lib/twterm/key_mapper/no_such_command.rb +20 -0
- data/lib/twterm/key_mapper/no_such_key.rb +16 -0
- data/lib/twterm/key_mapper/status_key_mapper.rb +18 -0
- data/lib/twterm/key_mapper/tab_key_mapper.rb +31 -0
- data/lib/twterm/key_mapper.rb +127 -0
- data/lib/twterm/list.rb +0 -31
- data/lib/twterm/notifier.rb +7 -7
- data/lib/twterm/repository/abstract_entity_repository.rb +41 -0
- data/lib/twterm/repository/abstract_expirable_entity_repository.rb +35 -0
- data/lib/twterm/repository/abstract_repository.rb +64 -0
- data/lib/twterm/repository/direct_message_repository.rb +14 -0
- data/lib/twterm/repository/friendship_repository.rb +108 -0
- data/lib/twterm/repository/hashtag_repository.rb +39 -0
- data/lib/twterm/repository/list_repository.rb +14 -0
- data/lib/twterm/repository/status_repository.rb +36 -0
- data/lib/twterm/repository/user_repository.rb +22 -0
- data/lib/twterm/rest_client.rb +107 -63
- data/lib/twterm/screen.rb +21 -15
- data/lib/twterm/search_query_window.rb +139 -0
- data/lib/twterm/status.rb +14 -108
- data/lib/twterm/streaming_client.rb +13 -12
- data/lib/twterm/tab/base.rb +48 -8
- data/lib/twterm/tab/direct_message/conversation.rb +53 -52
- data/lib/twterm/tab/direct_message/conversation_list.rb +46 -45
- data/lib/twterm/tab/dumpable.rb +3 -3
- data/lib/twterm/tab/key_assignments_cheatsheet.rb +58 -57
- data/lib/twterm/tab/loadable.rb +20 -0
- data/lib/twterm/tab/new/list.rb +32 -43
- data/lib/twterm/tab/new/search.rb +31 -44
- data/lib/twterm/tab/new/start.rb +44 -55
- data/lib/twterm/tab/new/user.rb +15 -12
- data/lib/twterm/tab/rate_limit_status.rb +84 -0
- data/lib/twterm/tab/scrollable.rb +39 -19
- data/lib/twterm/tab/searchable.rb +133 -0
- data/lib/twterm/tab/statuses/base.rb +139 -129
- data/lib/twterm/tab/statuses/conversation.rb +26 -15
- data/lib/twterm/tab/statuses/favorites.rb +10 -8
- data/lib/twterm/tab/statuses/home.rb +10 -9
- data/lib/twterm/tab/statuses/list_timeline.rb +12 -8
- data/lib/twterm/tab/statuses/mentions.rb +17 -11
- data/lib/twterm/tab/statuses/search.rb +8 -5
- data/lib/twterm/tab/statuses/user_timeline.rb +11 -8
- data/lib/twterm/tab/user_list_management.rb +109 -0
- data/lib/twterm/tab/user_tab.rb +125 -126
- data/lib/twterm/tab/users/base.rb +39 -41
- data/lib/twterm/tab/users/followers.rb +9 -6
- data/lib/twterm/tab/users/friends.rb +9 -6
- data/lib/twterm/tab_manager.rb +64 -40
- data/lib/twterm/tweetbox.rb +18 -13
- data/lib/twterm/uri_opener.rb +2 -1
- data/lib/twterm/user.rb +2 -110
- data/lib/twterm/version.rb +1 -1
- data/lib/twterm/view.rb +30 -0
- data/lib/twterm.rb +3 -9
- data/spec/fixtures/status.json +107 -0
- data/spec/fixtures/user.json +102 -0
- data/spec/spec_helper.rb +7 -0
- data/spec/supports/shared_examples/abstract_key_mapper.rb +17 -0
- data/spec/twterm/extension/enumerator/lazy_spec.rb +11 -0
- data/spec/twterm/friendship_spec.rb +0 -102
- data/spec/twterm/image/blank_line_spec.rb +11 -0
- data/spec/twterm/image/brackets_spec.rb +12 -0
- data/spec/twterm/image/color_spec.rb +22 -0
- data/spec/twterm/image/empry_spec.rb +11 -0
- data/spec/twterm/image/horizontal_sequential_image_spec.rb +15 -0
- data/spec/twterm/image/parens_spec.rb +12 -0
- data/spec/twterm/image/string_image_spec.rb +12 -0
- data/spec/twterm/image/vertical_sequential_image_spec.rb +14 -0
- data/spec/twterm/image_spec.rb +65 -0
- data/spec/twterm/key_mapper/abstract_key_mapper_spec.rb +21 -0
- data/spec/twterm/key_mapper/app_key_mapper_spec.rb +7 -0
- data/spec/twterm/key_mapper/status_key_mapper_spec.rb +7 -0
- data/spec/twterm/key_mapper/tab_key_mapper_spec.rb +7 -0
- data/spec/twterm/repository/friendship_repository_spec.rb +108 -0
- data/spec/twterm/status_spec.rb +58 -0
- data/spec/twterm/user_spec.rb +94 -0
- data/twterm.gemspec +13 -10
- metadata +129 -35
- data/lib/twterm/event/notification.rb +0 -33
- data/lib/twterm/extensions/integer.rb +0 -5
- data/lib/twterm/filter_query_window.rb +0 -91
- data/lib/twterm/filterable_list.rb +0 -41
- data/lib/twterm/history/base.rb +0 -21
- data/lib/twterm/history/hashtag.rb +0 -13
- data/lib/twterm/history/savable.rb +0 -37
- data/lib/twterm/history/screen_name.rb +0 -11
- data/lib/twterm/promise.rb +0 -143
@@ -0,0 +1,84 @@
|
|
1
|
+
require 'twterm/tab/loadable'
|
2
|
+
|
3
|
+
module Twterm
|
4
|
+
module Tab
|
5
|
+
class RateLimitStatus < Tab::Base
|
6
|
+
include Loadable
|
7
|
+
include Scrollable
|
8
|
+
|
9
|
+
@@mutex = Mutex.new
|
10
|
+
@@status = nil
|
11
|
+
|
12
|
+
def initialize(app, client)
|
13
|
+
super(app, client)
|
14
|
+
|
15
|
+
scroller.set_no_cursor_mode!
|
16
|
+
|
17
|
+
@ticker = Scheduler.new(1) { render }
|
18
|
+
|
19
|
+
@scheduler = Scheduler.new(10) { fetch }
|
20
|
+
|
21
|
+
fetch.then { initially_loaded! }
|
22
|
+
end
|
23
|
+
|
24
|
+
def close
|
25
|
+
@scheduler.kill
|
26
|
+
@ticker.kill
|
27
|
+
super
|
28
|
+
end
|
29
|
+
|
30
|
+
def drawable_item_count
|
31
|
+
window.maxy - 2
|
32
|
+
end
|
33
|
+
|
34
|
+
def image
|
35
|
+
return Image.string('Loading...') unless initially_loaded?
|
36
|
+
|
37
|
+
items
|
38
|
+
.drop(scroller.offset)
|
39
|
+
.take(drawable_item_count)
|
40
|
+
.reduce(Image.empty, :|)
|
41
|
+
end
|
42
|
+
|
43
|
+
def items
|
44
|
+
return [] unless initially_loaded?
|
45
|
+
|
46
|
+
@@status[:resources].flat_map do |category, limits|
|
47
|
+
[
|
48
|
+
!Image.string(category.to_s.gsub('_', ' ')).color(:green),
|
49
|
+
Image.blank_line,
|
50
|
+
*limits.flat_map do |endpoint, data|
|
51
|
+
limit, remaining = data[:limit], data[:remaining]
|
52
|
+
t = Time.at(data[:reset])
|
53
|
+
diff = (t - Time.now).round
|
54
|
+
|
55
|
+
[
|
56
|
+
Image.string(" #{endpoint}").color(:cyan) - (
|
57
|
+
limit == remaining \
|
58
|
+
? Image.empty
|
59
|
+
: Image.string(" (Resets #{diff.positive? ? "in #{diff} seconds" : 'soon...'})")),
|
60
|
+
Image.string(' ') - Image.remaining_resource(remaining, limit, 50) - Image.string(" #{remaining}/#{limit}"),
|
61
|
+
Image.blank_line,
|
62
|
+
]
|
63
|
+
end,
|
64
|
+
Image.blank_line,
|
65
|
+
]
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def respond_to_key(key)
|
70
|
+
scroller.respond_to_key(key)
|
71
|
+
end
|
72
|
+
|
73
|
+
def title
|
74
|
+
'Rate Limit Status'.freeze
|
75
|
+
end
|
76
|
+
|
77
|
+
private
|
78
|
+
|
79
|
+
def fetch
|
80
|
+
client.rate_limit_status.then { |status| @@mutex.synchronize { @@status = status } }
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -9,9 +9,9 @@ module Twterm
|
|
9
9
|
def scroller
|
10
10
|
return @scroller unless @scroller.nil?
|
11
11
|
|
12
|
-
@scroller = Scroller.new
|
12
|
+
@scroller = self.class::Scroller.new(self)
|
13
13
|
@scroller.delegate = self
|
14
|
-
@scroller.after_move {
|
14
|
+
@scroller.after_move { render }
|
15
15
|
@scroller
|
16
16
|
end
|
17
17
|
|
@@ -21,10 +21,9 @@ module Twterm
|
|
21
21
|
items.count
|
22
22
|
end
|
23
23
|
|
24
|
-
private
|
25
|
-
|
26
24
|
class Scroller
|
27
25
|
extend Forwardable
|
26
|
+
include Publisher
|
28
27
|
|
29
28
|
attr_reader :index, :offset
|
30
29
|
|
@@ -35,19 +34,21 @@ module Twterm
|
|
35
34
|
add_hook(:after_move, &block)
|
36
35
|
end
|
37
36
|
|
38
|
-
def
|
39
|
-
|
37
|
+
def current_index?(i)
|
38
|
+
index == offset + i
|
40
39
|
end
|
41
40
|
|
42
|
-
def current_item
|
43
|
-
index
|
41
|
+
def current_item
|
42
|
+
items[index]
|
44
43
|
end
|
45
44
|
|
46
45
|
def no_cursor_mode?
|
47
46
|
!!@no_cursor_mode
|
48
47
|
end
|
49
48
|
|
50
|
-
def initialize
|
49
|
+
def initialize(tab)
|
50
|
+
@tab = tab
|
51
|
+
|
51
52
|
@index = 0
|
52
53
|
@offset = 0
|
53
54
|
@no_cursor_mode = false
|
@@ -58,7 +59,6 @@ module Twterm
|
|
58
59
|
end
|
59
60
|
|
60
61
|
def item_appended!
|
61
|
-
@index -= 1
|
62
62
|
@offset -= 1 if @offset > 0
|
63
63
|
end
|
64
64
|
|
@@ -69,7 +69,7 @@ module Twterm
|
|
69
69
|
end
|
70
70
|
|
71
71
|
def move_down
|
72
|
-
return if count == 0 || index == count - 1
|
72
|
+
return if count == 0 || index == count - 1 || (no_cursor_mode? && offset + drawable_item_count >= count)
|
73
73
|
# return when there are no items or cursor is at the bottom
|
74
74
|
|
75
75
|
@index += 1
|
@@ -81,7 +81,7 @@ module Twterm
|
|
81
81
|
def move_to_bottom
|
82
82
|
return if count == 0 || index == count - 1
|
83
83
|
|
84
|
-
@index = count - 1
|
84
|
+
@index = no_cursor_mode? ? count - drawable_item_count : count - 1
|
85
85
|
@offset = [count - drawable_item_count + 1, 0].max
|
86
86
|
|
87
87
|
@offset += 1 until last_item_shown?
|
@@ -89,6 +89,16 @@ module Twterm
|
|
89
89
|
hook :after_move
|
90
90
|
end
|
91
91
|
|
92
|
+
def move_to(n)
|
93
|
+
if nth_item_drawable?(n)
|
94
|
+
@index = n
|
95
|
+
else
|
96
|
+
@index = @offset = n
|
97
|
+
end
|
98
|
+
|
99
|
+
hook :after_move
|
100
|
+
end
|
101
|
+
|
92
102
|
def move_to_top
|
93
103
|
return if count.zero? || index.zero?
|
94
104
|
|
@@ -108,23 +118,31 @@ module Twterm
|
|
108
118
|
end
|
109
119
|
|
110
120
|
def nth_item_drawable?(n)
|
111
|
-
n.between?(offset, offset + drawable_item_count)
|
121
|
+
n.between?(offset, offset + drawable_item_count - 1)
|
112
122
|
end
|
113
123
|
|
114
124
|
def respond_to_key(key)
|
125
|
+
k = KeyMapper.instance
|
126
|
+
|
115
127
|
case key
|
116
|
-
when
|
128
|
+
when k[:general, :page_down]
|
117
129
|
10.times { move_down }
|
118
|
-
when
|
130
|
+
when k[:general, :top]
|
119
131
|
move_to_top
|
120
|
-
when
|
132
|
+
when k[:general, :bottom]
|
121
133
|
move_to_bottom
|
122
|
-
when
|
134
|
+
when k[:general, :down], Curses::Key::DOWN
|
123
135
|
move_down
|
124
|
-
when
|
136
|
+
when k[:general, :up], Curses::Key::UP
|
125
137
|
move_up
|
126
|
-
when
|
138
|
+
when k[:general, :page_up]
|
127
139
|
10.times { move_up }
|
140
|
+
when k[:cursor, :top_of_window]
|
141
|
+
move_to(offset)
|
142
|
+
when k[:cursor, :middle_of_window]
|
143
|
+
move_to((2 * offset + [drawable_item_count, total_item_count - offset].min - 1) / 2)
|
144
|
+
when k[:cursor, :bottom_of_window]
|
145
|
+
move_to(offset + [drawable_item_count, total_item_count - offset].min - 1)
|
128
146
|
else
|
129
147
|
return false
|
130
148
|
end
|
@@ -138,6 +156,8 @@ module Twterm
|
|
138
156
|
|
139
157
|
private
|
140
158
|
|
159
|
+
attr_reader :tab
|
160
|
+
|
141
161
|
def add_hook(name, &block)
|
142
162
|
@hooks ||= {}
|
143
163
|
@hooks[name] = block
|
@@ -0,0 +1,133 @@
|
|
1
|
+
require 'twterm/event/notification/info'
|
2
|
+
require 'twterm/event/notification/warning'
|
3
|
+
require 'twterm/search_query_window'
|
4
|
+
|
5
|
+
module Twterm
|
6
|
+
module Tab
|
7
|
+
module Searchable
|
8
|
+
include Scrollable
|
9
|
+
extend Forwardable
|
10
|
+
|
11
|
+
def_delegators :scroller, :search_query
|
12
|
+
|
13
|
+
def matches?(item, query)
|
14
|
+
raise NotImplementedError, '`matches?` method must be implemented'
|
15
|
+
end
|
16
|
+
|
17
|
+
class Scroller < Scrollable::Scroller
|
18
|
+
extend Forwardable
|
19
|
+
include Publisher
|
20
|
+
|
21
|
+
attr_reader :search_query
|
22
|
+
|
23
|
+
def_delegators :search_query_window,
|
24
|
+
:searching_backward!, :searching_backward?,
|
25
|
+
:searching_down!, :searching_down?,
|
26
|
+
:searching_forward!, :searching_forward?,
|
27
|
+
:searching_up!, :searching_up?
|
28
|
+
|
29
|
+
def initialize(*)
|
30
|
+
super
|
31
|
+
@search_query = ''
|
32
|
+
end
|
33
|
+
|
34
|
+
def find_next
|
35
|
+
searching_forward!
|
36
|
+
|
37
|
+
search_query_window.render_last_query
|
38
|
+
search
|
39
|
+
end
|
40
|
+
|
41
|
+
def find_previous
|
42
|
+
searching_backward!
|
43
|
+
|
44
|
+
search_query_window.render_last_query
|
45
|
+
search
|
46
|
+
end
|
47
|
+
|
48
|
+
def respond_to_key(key)
|
49
|
+
k = KeyMapper.instance
|
50
|
+
|
51
|
+
return if super
|
52
|
+
|
53
|
+
case key
|
54
|
+
when k[:tab, :find_next]
|
55
|
+
find_next
|
56
|
+
when k[:tab, :find_previous]
|
57
|
+
find_previous
|
58
|
+
when k[:tab, :search_down]
|
59
|
+
search_down
|
60
|
+
when k[:tab, :search_up]
|
61
|
+
search_up
|
62
|
+
else
|
63
|
+
return false
|
64
|
+
end
|
65
|
+
|
66
|
+
true
|
67
|
+
end
|
68
|
+
|
69
|
+
def search_up
|
70
|
+
searching_up!
|
71
|
+
searching_forward!
|
72
|
+
ask
|
73
|
+
|
74
|
+
return if search_query.empty?
|
75
|
+
|
76
|
+
find_next
|
77
|
+
end
|
78
|
+
|
79
|
+
def search_down
|
80
|
+
searching_down!
|
81
|
+
searching_forward!
|
82
|
+
ask
|
83
|
+
|
84
|
+
return if search_query.empty?
|
85
|
+
|
86
|
+
find_next
|
87
|
+
end
|
88
|
+
|
89
|
+
private
|
90
|
+
|
91
|
+
def ask
|
92
|
+
@search_query = search_query_window.input || ''
|
93
|
+
end
|
94
|
+
|
95
|
+
def search
|
96
|
+
@search_query = search_query_window.last_query if search_query.empty?
|
97
|
+
|
98
|
+
if @search_query.empty?
|
99
|
+
event = Event::Notification::Info.new("search query is empty. Press '/' or '?' to start searching.")
|
100
|
+
publish(event)
|
101
|
+
return
|
102
|
+
end
|
103
|
+
|
104
|
+
f = searching_down? ^ searching_forward? ? -> xs { xs.reverse } : -> xs { xs }
|
105
|
+
previous_index = index
|
106
|
+
|
107
|
+
xs = [
|
108
|
+
*f.(items.each_with_index.drop(index.succ) + items.each_with_index.take(index)),
|
109
|
+
[current_item, index]
|
110
|
+
]
|
111
|
+
_, index = xs.find { |x, _| tab.matches?(x, search_query) }
|
112
|
+
|
113
|
+
if index.nil?
|
114
|
+
publish(Event::Notification::Warning.new("No matches found: \"#{search_query}\""))
|
115
|
+
else
|
116
|
+
hit_bottom = Event::Notification::Info.new('search hit BOTTOM, continuing at TOP')
|
117
|
+
hit_top = Event::Notification::Info.new('search hit TOP, continuing at BOTTOM')
|
118
|
+
|
119
|
+
publish(hit_bottom) if (searching_down? ^ searching_backward?) && index <= previous_index
|
120
|
+
publish(hit_top) if (searching_up? ^ searching_backward?) && index >= previous_index
|
121
|
+
move_to(index)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def search_query_window
|
126
|
+
SearchQueryWindow.instance
|
127
|
+
end
|
128
|
+
|
129
|
+
alias_method :count, :total_item_count
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|