twterm 2.6.0 → 2.10.1
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/Gemfile +7 -0
- data/Makefile +21 -0
- data/README.md +52 -6
- data/bin/twterm +1 -3
- data/default.nix +21 -0
- data/gemset.nix +5130 -0
- data/lib/twterm/app.rb +13 -24
- data/lib/twterm/client.rb +1 -4
- 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/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 +7 -14
- metadata +31 -113
- 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/repository/direct_message_repository.rb +0 -14
- 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
data/lib/twterm/app.rb
CHANGED
@@ -1,16 +1,13 @@
|
|
1
1
|
require 'curses'
|
2
2
|
|
3
3
|
require 'twterm/completion_manager'
|
4
|
-
require 'twterm/direct_message_composer'
|
5
4
|
require 'twterm/environment'
|
6
5
|
require 'twterm/event/screen/refresh'
|
7
|
-
require 'twterm/event/screen/resize'
|
8
6
|
require 'twterm/message_window'
|
9
7
|
require 'twterm/notification_dispatcher'
|
10
8
|
require 'twterm/persistable_configuration_proxy'
|
11
9
|
require 'twterm/preferences'
|
12
10
|
require 'twterm/photo_viewer'
|
13
|
-
require 'twterm/repository/direct_message_repository'
|
14
11
|
require 'twterm/repository/friendship_repository'
|
15
12
|
require 'twterm/repository/hashtag_repository'
|
16
13
|
require 'twterm/repository/list_repository'
|
@@ -26,6 +23,12 @@ module Twterm
|
|
26
23
|
|
27
24
|
attr_reader :environment, :preferences, :screen
|
28
25
|
|
26
|
+
# return [Twterm::MessageWindow]
|
27
|
+
attr_reader :message_window
|
28
|
+
|
29
|
+
# return [Twterm::SearchQueryWindow]
|
30
|
+
attr_reader :search_query_window
|
31
|
+
|
29
32
|
DATA_DIR = "#{ENV['HOME']}/.twterm".freeze
|
30
33
|
|
31
34
|
def initialize
|
@@ -42,14 +45,6 @@ module Twterm
|
|
42
45
|
@completion_manager ||= CompletionManager.new(self)
|
43
46
|
end
|
44
47
|
|
45
|
-
def direct_message_composer
|
46
|
-
@direct_message_composer ||= DirectMessageComposer.new(self, client)
|
47
|
-
end
|
48
|
-
|
49
|
-
def direct_message_repository
|
50
|
-
@direct_messages_repository ||= Repository::DirectMessageRepository.new
|
51
|
-
end
|
52
|
-
|
53
48
|
def friendship_repository
|
54
49
|
@friendship_repository ||= Repository::FriendshipRepository.new
|
55
50
|
end
|
@@ -73,8 +68,8 @@ module Twterm
|
|
73
68
|
|
74
69
|
@screen = Screen.new(self, client)
|
75
70
|
|
76
|
-
SearchQueryWindow.
|
77
|
-
MessageWindow.
|
71
|
+
@search_query_window = SearchQueryWindow.new(screen.search_query_window_window)
|
72
|
+
@message_window = MessageWindow.new(screen.message_window_window)
|
78
73
|
|
79
74
|
@notification_dispatcher = NotificationDispatcher.new(preferences)
|
80
75
|
@photo_viewer = PhotoViewer.new(preferences)
|
@@ -103,11 +98,6 @@ module Twterm
|
|
103
98
|
user_repository.expire(3600)
|
104
99
|
end
|
105
100
|
|
106
|
-
direct_message_repository.before_create do |dm|
|
107
|
-
user_repository.create(dm.recipient)
|
108
|
-
user_repository.create(dm.sender)
|
109
|
-
end
|
110
|
-
|
111
101
|
user_repository.before_create do |user|
|
112
102
|
client_id = client.user_id
|
113
103
|
|
@@ -158,7 +148,7 @@ module Twterm
|
|
158
148
|
end
|
159
149
|
|
160
150
|
def tab_manager
|
161
|
-
@tab_manager ||= TabManager.new(self, client)
|
151
|
+
@tab_manager ||= TabManager.new(self, client, screen.tab_manager_window)
|
162
152
|
end
|
163
153
|
|
164
154
|
def tweetbox
|
@@ -179,7 +169,6 @@ module Twterm
|
|
179
169
|
config[:access_token_secret],
|
180
170
|
{
|
181
171
|
friendship: friendship_repository,
|
182
|
-
direct_message: direct_message_repository,
|
183
172
|
hashtag: hashtag_repository,
|
184
173
|
list: list_repository,
|
185
174
|
status: status_repository,
|
@@ -193,11 +182,11 @@ module Twterm
|
|
193
182
|
end
|
194
183
|
|
195
184
|
def on_resize
|
196
|
-
|
185
|
+
lines, cols = `stty size`.split(' ').map(&:to_i)
|
186
|
+
|
187
|
+
Readline.set_screen_size(lines, cols)
|
197
188
|
|
198
|
-
lines
|
199
|
-
cols = `tput cols`.to_i
|
200
|
-
publish(Event::Screen::Resize.new(lines, cols))
|
189
|
+
screen.resize(lines, cols) unless Curses.closed?
|
201
190
|
end
|
202
191
|
end
|
203
192
|
end
|
data/lib/twterm/client.rb
CHANGED
@@ -11,7 +11,6 @@ module Twterm
|
|
11
11
|
@access_token, @access_token_secret = access_token, access_token_secret
|
12
12
|
|
13
13
|
@friendship_repository = repositories[:friendship]
|
14
|
-
@direct_message_repository = repositories[:direct_message]
|
15
14
|
@hashtag_repository = repositories[:hashtag]
|
16
15
|
@list_repository = repositories[:list]
|
17
16
|
@status_repository = repositories[:status]
|
@@ -27,12 +26,10 @@ module Twterm
|
|
27
26
|
muted_user_ids.include?(status.retweeted_status.user.id))
|
28
27
|
end
|
29
28
|
end
|
30
|
-
|
31
|
-
direct_message_manager
|
32
29
|
end
|
33
30
|
|
34
31
|
private
|
35
32
|
|
36
|
-
attr_reader :friendship_repository, :
|
33
|
+
attr_reader :friendship_repository, :hashtag_repository, :list_repository, :status_repository, :user_repository
|
37
34
|
end
|
38
35
|
end
|
data/lib/twterm/color_manager.rb
CHANGED
@@ -1,18 +1,17 @@
|
|
1
1
|
module Twterm
|
2
2
|
class ColorManager
|
3
3
|
include Singleton
|
4
|
-
include Curses
|
5
4
|
|
6
5
|
COLORS = [:black, :white, :red, :green, :blue, :yellow, :cyan, :magenta, :transparent]
|
7
6
|
CURSES_COLORS = {
|
8
|
-
black: COLOR_BLACK,
|
9
|
-
white: COLOR_WHITE,
|
10
|
-
red: COLOR_RED,
|
11
|
-
green: COLOR_GREEN,
|
12
|
-
blue: COLOR_BLUE,
|
13
|
-
yellow: COLOR_YELLOW,
|
14
|
-
cyan: COLOR_CYAN,
|
15
|
-
magenta: COLOR_MAGENTA,
|
7
|
+
black: Curses::COLOR_BLACK,
|
8
|
+
white: Curses::COLOR_WHITE,
|
9
|
+
red: Curses::COLOR_RED,
|
10
|
+
green: Curses::COLOR_GREEN,
|
11
|
+
blue: Curses::COLOR_BLUE,
|
12
|
+
yellow: Curses::COLOR_YELLOW,
|
13
|
+
cyan: Curses::COLOR_CYAN,
|
14
|
+
magenta: Curses::COLOR_MAGENTA,
|
16
15
|
transparent: -1
|
17
16
|
}
|
18
17
|
|
data/lib/twterm/image.rb
CHANGED
@@ -3,10 +3,12 @@ require 'twterm/image/blank_line'
|
|
3
3
|
require 'twterm/image/bold'
|
4
4
|
require 'twterm/image/brackets'
|
5
5
|
require 'twterm/image/color'
|
6
|
+
require 'twterm/image/dim'
|
6
7
|
require 'twterm/image/empty'
|
7
8
|
require 'twterm/image/horizontal_sequential_image'
|
8
9
|
require 'twterm/image/parens'
|
9
10
|
require 'twterm/image/string_image'
|
11
|
+
require 'twterm/image/underlined'
|
10
12
|
require 'twterm/image/vertical_sequential_image'
|
11
13
|
|
12
14
|
class Twterm::Image
|
@@ -14,6 +16,10 @@ class Twterm::Image
|
|
14
16
|
@column, @line = column, line
|
15
17
|
end
|
16
18
|
|
19
|
+
def _
|
20
|
+
underlined
|
21
|
+
end
|
22
|
+
|
17
23
|
def !
|
18
24
|
bold
|
19
25
|
end
|
@@ -44,6 +50,10 @@ class Twterm::Image
|
|
44
50
|
Brackets.new(self)
|
45
51
|
end
|
46
52
|
|
53
|
+
def dim(on = true)
|
54
|
+
on ? Dim.new(self) : self
|
55
|
+
end
|
56
|
+
|
47
57
|
def self.checkbox(checked)
|
48
58
|
string(checked ? '*' : ' ').brackets
|
49
59
|
end
|
@@ -101,6 +111,27 @@ class Twterm::Image
|
|
101
111
|
StringImage.new(str)
|
102
112
|
end
|
103
113
|
|
114
|
+
# @param items [Array<Symbol>]
|
115
|
+
# @param selected [Symbol]
|
116
|
+
#
|
117
|
+
# @return [Image]
|
118
|
+
def self.toggle_switch(items, selected)
|
119
|
+
items
|
120
|
+
.map do |item|
|
121
|
+
on = item == selected
|
122
|
+
string(item.to_s)
|
123
|
+
.bold(on)
|
124
|
+
.underlined(on)
|
125
|
+
end
|
126
|
+
.intersperse(string(' | '))
|
127
|
+
.reduce(empty) { |acc, x| acc - x }
|
128
|
+
.brackets
|
129
|
+
end
|
130
|
+
|
131
|
+
def underlined(on = true)
|
132
|
+
on ? Underlined.new(self) : self
|
133
|
+
end
|
134
|
+
|
104
135
|
def self.whitespace
|
105
136
|
string(' ')
|
106
137
|
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'twterm/image'
|
2
|
+
|
3
|
+
class Twterm::Image::Attr < Twterm::Image
|
4
|
+
# @param image [Twterm::Image]
|
5
|
+
def initialize(image)
|
6
|
+
super()
|
7
|
+
|
8
|
+
@image = image
|
9
|
+
end
|
10
|
+
|
11
|
+
def height
|
12
|
+
image.height
|
13
|
+
end
|
14
|
+
|
15
|
+
def render(window)
|
16
|
+
image, attr =
|
17
|
+
if image.is_a?(self.class) # fuse attributes when possible
|
18
|
+
[image.image, self.attr | image.attr]
|
19
|
+
else
|
20
|
+
[self.image, self.attr]
|
21
|
+
end
|
22
|
+
|
23
|
+
window.attron(attr)
|
24
|
+
image.at(line, column).render(window)
|
25
|
+
window.attroff(attr)
|
26
|
+
end
|
27
|
+
|
28
|
+
def width
|
29
|
+
image.width
|
30
|
+
end
|
31
|
+
|
32
|
+
protected
|
33
|
+
|
34
|
+
attr_reader :image
|
35
|
+
|
36
|
+
# @abstract
|
37
|
+
#
|
38
|
+
# @return [Integer]
|
39
|
+
def attr
|
40
|
+
raise NotImplementedError, '`attr` must be implemented'
|
41
|
+
end
|
42
|
+
end
|
data/lib/twterm/image/bold.rb
CHANGED
@@ -1,31 +1,17 @@
|
|
1
|
+
require 'twterm/image/attr'
|
2
|
+
|
1
3
|
module Twterm
|
2
4
|
class Image
|
3
|
-
class Bold < Twterm::Image
|
4
|
-
def initialize(image)
|
5
|
-
@image = image
|
6
|
-
end
|
7
|
-
|
8
|
-
def height
|
9
|
-
image.height
|
10
|
-
end
|
11
|
-
|
12
|
-
def render(window)
|
13
|
-
window.attron(Curses::A_BOLD)
|
14
|
-
image.at(line, column).render(window)
|
15
|
-
window.attroff(Curses::A_BOLD)
|
16
|
-
end
|
17
|
-
|
5
|
+
class Bold < Twterm::Image::Attr
|
18
6
|
def to_s
|
19
7
|
"\e[1m#{image}\e[0m"
|
20
8
|
end
|
21
9
|
|
22
|
-
|
23
|
-
image.width
|
24
|
-
end
|
10
|
+
protected
|
25
11
|
|
26
|
-
|
27
|
-
|
28
|
-
|
12
|
+
def attr
|
13
|
+
Curses::A_BOLD
|
14
|
+
end
|
29
15
|
end
|
30
16
|
end
|
31
17
|
end
|
data/lib/twterm/image/color.rb
CHANGED
@@ -1,18 +1,12 @@
|
|
1
|
+
require 'twterm/image/attr'
|
2
|
+
|
1
3
|
module Twterm
|
2
4
|
class Image
|
3
|
-
class Color < Twterm::Image
|
5
|
+
class Color < Twterm::Image::Attr
|
4
6
|
def initialize(image, fg, bg = :transparent)
|
5
|
-
|
6
|
-
end
|
7
|
-
|
8
|
-
def height
|
9
|
-
image.height
|
10
|
-
end
|
7
|
+
super(image)
|
11
8
|
|
12
|
-
|
13
|
-
window.attron(Curses.color_pair(color_pair_index))
|
14
|
-
image.at(line, column).render(window)
|
15
|
-
window.attroff(Curses.color_pair(color_pair_index))
|
9
|
+
@fg, @bg = fg, bg
|
16
10
|
end
|
17
11
|
|
18
12
|
def to_s
|
@@ -29,14 +23,14 @@ module Twterm
|
|
29
23
|
@bg == :transparent ? str : "\e[#{bg_colors[@bg]}m#{str}"
|
30
24
|
end
|
31
25
|
|
32
|
-
|
33
|
-
|
26
|
+
protected
|
27
|
+
|
28
|
+
def attr
|
29
|
+
Curses.color_pair(color_pair_index)
|
34
30
|
end
|
35
31
|
|
36
32
|
private
|
37
33
|
|
38
|
-
attr_reader :image
|
39
|
-
|
40
34
|
def color_pair_index
|
41
35
|
Twterm::ColorManager.instance.get_color_pair_index(@fg, @bg)
|
42
36
|
end
|
data/lib/twterm/key_mapper.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
require 'toml'
|
1
|
+
require 'toml-rb'
|
2
2
|
require 'singleton'
|
3
3
|
|
4
4
|
require 'twterm/app'
|
@@ -105,14 +105,12 @@ module Twterm
|
|
105
105
|
end
|
106
106
|
|
107
107
|
def load_dict_file!
|
108
|
-
dict =
|
109
|
-
rescue
|
108
|
+
dict = TomlRB.load_file(dict_file_path, symbolize_keys: true)
|
109
|
+
rescue TomlRB::ParseError => e
|
110
110
|
first_line =
|
111
111
|
case e
|
112
|
-
when
|
113
|
-
"Your key assignments dictionary file (#{dict_file_path}) could not be parsed"
|
114
|
-
when TOML::ValueOverwriteError
|
115
|
-
"Command `#{e.key}` is declared more than once"
|
112
|
+
when TomlRB::ParseError
|
113
|
+
"Your key assignments dictionary file (#{dict_file_path}) could not be parsed:\n#{e.message}"
|
116
114
|
end
|
117
115
|
|
118
116
|
warn <<-EOS
|
@@ -132,7 +130,7 @@ Press any key to continue
|
|
132
130
|
end
|
133
131
|
|
134
132
|
def save!(mappings)
|
135
|
-
dict =
|
133
|
+
dict = TomlRB.dump(mappings).gsub("\n[", "\n\n[")
|
136
134
|
File.open(dict_file_path, 'w', 0644) { |f| f.write(dict) }
|
137
135
|
end
|
138
136
|
end
|
@@ -35,8 +35,6 @@ module Twterm
|
|
35
35
|
|
36
36
|
def translate(key)
|
37
37
|
case key
|
38
|
-
when '!'..'}' then key
|
39
|
-
when /\A\^([A-Z]?)\Z/ then $1.ord - 'A'.ord + 1
|
40
38
|
when 'F1' then Curses::Key::F1
|
41
39
|
when 'F2' then Curses::Key::F2
|
42
40
|
when 'F3' then Curses::Key::F3
|
@@ -49,6 +47,8 @@ module Twterm
|
|
49
47
|
when 'F10' then Curses::Key::F10
|
50
48
|
when 'F11' then Curses::Key::F11
|
51
49
|
when 'F12' then Curses::Key::F12
|
50
|
+
when /\A\^([A-Z]?)\Z/ then $1.ord - 'A'.ord + 1
|
51
|
+
when '!'..'}' then key
|
52
52
|
else
|
53
53
|
raise NoSuchKey.new(key)
|
54
54
|
end
|
data/lib/twterm/list.rb
CHANGED
@@ -1,7 +1,41 @@
|
|
1
1
|
module Twterm
|
2
|
+
# A Twitter list
|
2
3
|
class List
|
3
|
-
|
4
|
+
# Unique ID of the list
|
5
|
+
#
|
6
|
+
# @return [Integer]
|
7
|
+
attr_reader :id
|
4
8
|
|
9
|
+
# @return [String]
|
10
|
+
attr_reader :name
|
11
|
+
|
12
|
+
# @return [String]
|
13
|
+
attr_reader :slug
|
14
|
+
|
15
|
+
# @return [String]
|
16
|
+
attr_reader :full_name
|
17
|
+
|
18
|
+
attr_reader :mode
|
19
|
+
|
20
|
+
# @return [String]
|
21
|
+
attr_reader :description
|
22
|
+
|
23
|
+
# The number of users that are in this list
|
24
|
+
#
|
25
|
+
# @return [Integer]
|
26
|
+
attr_reader :member_count
|
27
|
+
|
28
|
+
# The number of users that subscribe this list
|
29
|
+
#
|
30
|
+
# @return [Integer]
|
31
|
+
attr_reader :subscriber_count
|
32
|
+
|
33
|
+
# @return [String]
|
34
|
+
attr_reader :url
|
35
|
+
|
36
|
+
# @param other [List]
|
37
|
+
#
|
38
|
+
# @return [Boolean]
|
5
39
|
def ==(other)
|
6
40
|
other.is_a?(self.class) && id == other.id
|
7
41
|
end
|
@@ -11,6 +45,7 @@ module Twterm
|
|
11
45
|
update!(list)
|
12
46
|
end
|
13
47
|
|
48
|
+
# @return [self]
|
14
49
|
def update!(list)
|
15
50
|
@name = list.name
|
16
51
|
@slug = list.slug
|