twterm 2.5.1 → 2.10.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.
- checksums.yaml +4 -4
- data/.circleci/config.yml +62 -0
- data/.gitignore +1 -0
- data/Makefile +21 -0
- data/README.md +52 -6
- data/bin/twterm +1 -3
- data/default.nix +21 -0
- data/gemset.nix +4981 -0
- data/lib/twterm/app.rb +13 -26
- data/lib/twterm/client.rb +1 -6
- data/lib/twterm/color_manager.rb +8 -9
- data/lib/twterm/image.rb +31 -0
- data/lib/twterm/image/attr.rb +42 -0
- data/lib/twterm/image/bold.rb +7 -21
- data/lib/twterm/image/color.rb +9 -15
- data/lib/twterm/image/dim.rb +13 -0
- data/lib/twterm/image/underlined.rb +17 -0
- data/lib/twterm/image_builder/user_name_image_builder.rb +1 -0
- data/lib/twterm/key_mapper.rb +6 -8
- data/lib/twterm/key_mapper/abstract_key_mapper.rb +2 -2
- data/lib/twterm/list.rb +36 -1
- data/lib/twterm/message_window.rb +4 -13
- data/lib/twterm/persistable_configuration_proxy.rb +6 -6
- data/lib/twterm/preferences.rb +8 -1
- data/lib/twterm/rest_client.rb +0 -33
- data/lib/twterm/screen.rb +103 -25
- data/lib/twterm/search_query_window.rb +5 -13
- data/lib/twterm/status.rb +10 -0
- data/lib/twterm/tab/abstract_tab.rb +30 -16
- data/lib/twterm/tab/new/index.rb +0 -10
- data/lib/twterm/tab/new/search.rb +2 -2
- data/lib/twterm/tab/new/user.rb +2 -2
- data/lib/twterm/tab/preferences/control.rb +77 -0
- data/lib/twterm/tab/preferences/index.rb +6 -0
- data/lib/twterm/tab/scrollable.rb +1 -1
- data/lib/twterm/tab/searchable.rb +6 -4
- data/lib/twterm/tab/status_tab.rb +10 -0
- data/lib/twterm/tab/statuses/abstract_statuses_tab.rb +11 -6
- data/lib/twterm/tab/statuses/home.rb +0 -3
- data/lib/twterm/tab/statuses/mentions.rb +1 -0
- data/lib/twterm/tab/user_tab.rb +0 -9
- data/lib/twterm/tab_manager.rb +77 -10
- data/lib/twterm/tweetbox.rb +2 -3
- data/lib/twterm/user.rb +1 -0
- data/lib/twterm/version.rb +1 -1
- data/nix/Gemfile +3 -0
- data/nix/Gemfile.lock +77 -0
- data/nix/gemset.nix +325 -0
- data/shell.nix +40 -0
- data/spec/twterm/image/bold_spec.rb +30 -0
- data/spec/twterm/image/color_spec.rb +16 -0
- data/spec/twterm/image/dim_spec.rb +30 -0
- data/twterm.gemspec +13 -13
- metadata +46 -46
- data/.travis.yml +0 -12
- data/lib/twterm/direct_message.rb +0 -60
- data/lib/twterm/direct_message_composer.rb +0 -80
- data/lib/twterm/direct_message_manager.rb +0 -51
- data/lib/twterm/event/direct_message/fetched.rb +0 -10
- data/lib/twterm/event/notification/direct_message.rb +0 -30
- data/lib/twterm/event/screen/resize.rb +0 -13
- data/lib/twterm/event/status/timeline.rb +0 -10
- data/lib/twterm/repository/direct_message_repository.rb +0 -14
- data/lib/twterm/streaming_client.rb +0 -146
- data/lib/twterm/tab/direct_message/conversation.rb +0 -104
- data/lib/twterm/tab/direct_message/conversation_list.rb +0 -100
- data/spec/twterm/event/screen/resize_spec.rb +0 -11
@@ -1,26 +1,31 @@
|
|
1
1
|
require 'concurrent'
|
2
2
|
|
3
|
-
require 'twterm/event/screen/resize'
|
4
3
|
require 'twterm/image'
|
5
4
|
require 'twterm/subscriber'
|
6
5
|
|
7
6
|
module Twterm
|
8
7
|
module Tab
|
9
8
|
class AbstractTab
|
10
|
-
include Curses
|
11
9
|
include Subscriber
|
12
10
|
|
13
|
-
|
11
|
+
# @return [String]
|
12
|
+
attr_reader :title
|
14
13
|
|
14
|
+
# @param other [Twterm::Tab::AbstractTab]
|
15
|
+
#
|
16
|
+
# @return [Boolean]
|
15
17
|
def ==(other)
|
16
18
|
self.equal?(other)
|
17
19
|
end
|
18
20
|
|
21
|
+
# @return [void]
|
19
22
|
def close
|
20
23
|
unsubscribe
|
21
|
-
window.close
|
22
24
|
end
|
23
25
|
|
26
|
+
# A utility method to find a status by its ID
|
27
|
+
#
|
28
|
+
# @return [Concurrent::Promise<Twterm::Status>]
|
24
29
|
def find_or_fetch_status(id)
|
25
30
|
status = app.status_repository.find(id)
|
26
31
|
|
@@ -31,6 +36,9 @@ module Twterm
|
|
31
36
|
end
|
32
37
|
end
|
33
38
|
|
39
|
+
# A utility method to find a list by their ID
|
40
|
+
#
|
41
|
+
# @return [Concurrent::Promise<Twterm::List>]
|
34
42
|
def find_or_fetch_list(id)
|
35
43
|
list = app.list_repository.find(id)
|
36
44
|
|
@@ -41,6 +49,9 @@ module Twterm
|
|
41
49
|
end
|
42
50
|
end
|
43
51
|
|
52
|
+
# A utility method to find a user by their id
|
53
|
+
#
|
54
|
+
# @return [Concurrent::Promise<Twterm::User>]
|
44
55
|
def find_or_fetch_user(id)
|
45
56
|
user = app.user_repository.find(id)
|
46
57
|
|
@@ -53,10 +64,6 @@ module Twterm
|
|
53
64
|
|
54
65
|
def initialize(app, client)
|
55
66
|
@app, @client = app, client
|
56
|
-
|
57
|
-
@window = stdscr.subwin(stdscr.maxy - 5, stdscr.maxx, 3, 0)
|
58
|
-
|
59
|
-
subscribe(Event::Screen::Resize, :resize)
|
60
67
|
end
|
61
68
|
|
62
69
|
def render
|
@@ -72,7 +79,7 @@ module Twterm
|
|
72
79
|
end
|
73
80
|
end
|
74
81
|
|
75
|
-
view.
|
82
|
+
view.render
|
76
83
|
end if refreshable?
|
77
84
|
end
|
78
85
|
end
|
@@ -88,8 +95,13 @@ module Twterm
|
|
88
95
|
|
89
96
|
private
|
90
97
|
|
91
|
-
|
98
|
+
# @return [Twterm::App]
|
99
|
+
attr_reader :app
|
92
100
|
|
101
|
+
# @return [Twterm::Client]
|
102
|
+
attr_reader :client
|
103
|
+
|
104
|
+
# @return [Twterm::Image]
|
93
105
|
def image
|
94
106
|
Image.string('view method is not implemented')
|
95
107
|
end
|
@@ -98,22 +110,24 @@ module Twterm
|
|
98
110
|
@refresh_mutex ||= Mutex.new
|
99
111
|
end
|
100
112
|
|
113
|
+
# @return [Boolean]
|
101
114
|
def refreshable?
|
102
115
|
!(
|
103
116
|
refresh_mutex.locked? ||
|
104
|
-
closed? ||
|
117
|
+
Curses.closed? ||
|
105
118
|
app.tab_manager.current_tab.object_id != object_id
|
106
119
|
)
|
107
120
|
end
|
108
121
|
|
109
|
-
|
110
|
-
window.resize(stdscr.maxy - 5, stdscr.maxx)
|
111
|
-
window.move(3, 0)
|
112
|
-
end
|
113
|
-
|
122
|
+
# @return [Twterm::View]
|
114
123
|
def view
|
115
124
|
View.new(window, image)
|
116
125
|
end
|
126
|
+
|
127
|
+
# @todo This method is for transition. `window` should explicitly be obtained on initialization.
|
128
|
+
def window
|
129
|
+
app.screen.tab_window
|
130
|
+
end
|
117
131
|
end
|
118
132
|
end
|
119
133
|
end
|
data/lib/twterm/tab/new/index.rb
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
require 'twterm/tab/abstract_tab'
|
2
|
-
require 'twterm/tab/direct_message/conversation_list'
|
3
2
|
require 'twterm/tab/rate_limit_status'
|
4
3
|
require 'twterm/tab/preferences/index'
|
5
4
|
|
@@ -19,7 +18,6 @@ module Twterm
|
|
19
18
|
|
20
19
|
def items
|
21
20
|
%i(
|
22
|
-
direct_messages
|
23
21
|
list_tab
|
24
22
|
search_tab
|
25
23
|
user_tab
|
@@ -60,8 +58,6 @@ module Twterm
|
|
60
58
|
|
61
59
|
desc =
|
62
60
|
case item
|
63
|
-
when :direct_messages
|
64
|
-
'Direct messages'
|
65
61
|
when :list_tab
|
66
62
|
'List tab'
|
67
63
|
when :search_tab
|
@@ -82,10 +78,6 @@ module Twterm
|
|
82
78
|
.reduce(Image.empty, :|)
|
83
79
|
end
|
84
80
|
|
85
|
-
def open_direct_messages
|
86
|
-
switch(Tab::DirectMessage::ConversationList.new(app, client))
|
87
|
-
end
|
88
|
-
|
89
81
|
def open_list_tab
|
90
82
|
switch(Tab::New::List.new(app, client))
|
91
83
|
end
|
@@ -114,8 +106,6 @@ module Twterm
|
|
114
106
|
|
115
107
|
def perform_selected_action
|
116
108
|
case current_item
|
117
|
-
when :direct_messages
|
118
|
-
open_direct_messages
|
119
109
|
when :list_tab
|
120
110
|
open_list_tab
|
121
111
|
when :search_tab
|
@@ -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
|
data/lib/twterm/tab/new/user.rb
CHANGED
@@ -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
|
@@ -27,7 +27,7 @@ module Twterm
|
|
27
27
|
attr_reader :index, :offset
|
28
28
|
|
29
29
|
attr_accessor :delegate
|
30
|
-
def_delegators :delegate, :items, :total_item_count, :drawable_item_count
|
30
|
+
def_delegators :delegate, :items, :total_item_count, :drawable_item_count, :search_query_window
|
31
31
|
|
32
32
|
def after_move(&block)
|
33
33
|
add_hook(:after_move, &block)
|
@@ -14,6 +14,12 @@ module Twterm
|
|
14
14
|
raise NotImplementedError, '`matches?` method must be implemented'
|
15
15
|
end
|
16
16
|
|
17
|
+
# @abstract
|
18
|
+
# @return [Twterm::SearchQueryWindow]
|
19
|
+
def search_query_window
|
20
|
+
raise NotImplementedError, '`search_query_window` method must be implemented'
|
21
|
+
end
|
22
|
+
|
17
23
|
class Scroller < Scrollable::Scroller
|
18
24
|
extend Forwardable
|
19
25
|
include Publisher
|
@@ -122,10 +128,6 @@ module Twterm
|
|
122
128
|
end
|
123
129
|
end
|
124
130
|
|
125
|
-
def search_query_window
|
126
|
-
SearchQueryWindow.instance
|
127
|
-
end
|
128
|
-
|
129
131
|
alias_method :count, :total_item_count
|
130
132
|
end
|
131
133
|
end
|
@@ -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 -
|
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 -
|
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 -
|
116
|
+
status.split(window.maxx - 2)
|
117
117
|
scroller.item_prepended!
|
118
118
|
render
|
119
119
|
end
|
@@ -226,16 +226,16 @@ module Twterm
|
|
226
226
|
|
227
227
|
header = [
|
228
228
|
ImageBuilder::UserNameImageBuilder.new(user).build,
|
229
|
-
Image.string(original.date.to_s).brackets,
|
229
|
+
Image.string(original.date.to_s).brackets.dim,
|
230
230
|
(Image.whitespace.color(:black, :red) if original.favorited?),
|
231
231
|
(Image.whitespace.color(:black, :green) if original.retweeted?),
|
232
|
-
((Image.string('retweeted by ') - !Image.string("@#{retweeted_by.screen_name}")).parens if status.retweet?),
|
232
|
+
((Image.string('retweeted by ') - !Image.string("@#{retweeted_by.screen_name}")).parens.dim if status.retweet?),
|
233
233
|
((Image.number(original.favorite_count) - Image.plural(original.favorite_count, 'like')).color(:red) if original.favorite_count.positive?),
|
234
234
|
((Image.number(original.retweet_count) - Image.plural(original.retweet_count, 'RT')).color(:green) if original.retweet_count.positive?),
|
235
235
|
].compact.intersperse(Image.whitespace).reduce(Image.empty, :-)
|
236
236
|
|
237
237
|
body = original
|
238
|
-
.split(window.maxx -
|
238
|
+
.split(window.maxx - 2)
|
239
239
|
.map(&Image.method(:string))
|
240
240
|
.reduce(Image.empty, :|)
|
241
241
|
|
@@ -263,6 +263,11 @@ module Twterm
|
|
263
263
|
end
|
264
264
|
end
|
265
265
|
|
266
|
+
# for the sake of Twterm::Tab::Searchable
|
267
|
+
def search_query_window
|
268
|
+
app.search_query_window
|
269
|
+
end
|
270
|
+
|
266
271
|
def sort
|
267
272
|
return if items.empty? || scroller.current_item.nil?
|
268
273
|
|
@@ -1,5 +1,4 @@
|
|
1
1
|
require 'twterm/subscriber'
|
2
|
-
require 'twterm/event/status/timeline'
|
3
2
|
require 'twterm/tab/statuses/abstract_statuses_tab'
|
4
3
|
require 'twterm/utils'
|
5
4
|
|
@@ -21,8 +20,6 @@ module Twterm
|
|
21
20
|
def initialize(app, client)
|
22
21
|
super(app, client)
|
23
22
|
|
24
|
-
subscribe(Event::Status::Timeline) { |e| prepend(e.status) }
|
25
|
-
|
26
23
|
reload.then do
|
27
24
|
initially_loaded!
|
28
25
|
scroller.move_to_top
|