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/.travis.yml
DELETED
@@ -1,60 +0,0 @@
|
|
1
|
-
require 'twterm/user'
|
2
|
-
require 'twterm/utils'
|
3
|
-
|
4
|
-
module Twterm
|
5
|
-
class DirectMessage
|
6
|
-
attr_reader :id, :created_at, :recipient_id, :sender_id, :text
|
7
|
-
|
8
|
-
def initialize(message)
|
9
|
-
@id = message.id
|
10
|
-
update!(message)
|
11
|
-
end
|
12
|
-
|
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 update!(message)
|
23
|
-
@created_at = message.created_at.dup.localtime
|
24
|
-
@recipient_id = message.recipient.id
|
25
|
-
@sender_id = message.sender.id
|
26
|
-
@text = message.text
|
27
|
-
|
28
|
-
self
|
29
|
-
end
|
30
|
-
|
31
|
-
class Conversation
|
32
|
-
include Utils
|
33
|
-
|
34
|
-
attr_reader :collocutor_id, :messages
|
35
|
-
|
36
|
-
def initialize(collocutor_id)
|
37
|
-
@collocutor_id = collocutor_id
|
38
|
-
@messages = []
|
39
|
-
end
|
40
|
-
|
41
|
-
def <<(message)
|
42
|
-
@messages << message if messages.find { |m| m == message }.nil?
|
43
|
-
@messages.sort_by!(&:created_at).reverse!
|
44
|
-
|
45
|
-
self
|
46
|
-
end
|
47
|
-
|
48
|
-
def preview
|
49
|
-
messages.sort_by(&:created_at).last.text.gsub("\n", ' ')
|
50
|
-
end
|
51
|
-
|
52
|
-
def updated_at
|
53
|
-
updated_at = @messages.map(&:created_at).max
|
54
|
-
|
55
|
-
format = Time.now - updated_at < 86_400 ? '%H:%M:%S' : '%Y-%m-%d %H:%M:%S'
|
56
|
-
updated_at.strftime(format)
|
57
|
-
end
|
58
|
-
end
|
59
|
-
end
|
60
|
-
end
|
@@ -1,80 +0,0 @@
|
|
1
|
-
require 'twterm/event/screen/refresh'
|
2
|
-
require 'twterm/publisher'
|
3
|
-
require 'twterm/user'
|
4
|
-
require 'twterm/utils'
|
5
|
-
|
6
|
-
module Twterm
|
7
|
-
class DirectMessageComposer
|
8
|
-
include Readline
|
9
|
-
include Curses
|
10
|
-
include Publisher
|
11
|
-
include Utils
|
12
|
-
|
13
|
-
def initialize(app, client)
|
14
|
-
@app, @client = app, client
|
15
|
-
end
|
16
|
-
|
17
|
-
def compose(recipient)
|
18
|
-
check_type User, recipient
|
19
|
-
|
20
|
-
clear
|
21
|
-
|
22
|
-
resetter = proc do
|
23
|
-
reset_prog_mode
|
24
|
-
sleep 0.1
|
25
|
-
publish(Event::Screen::Refresh.new)
|
26
|
-
end
|
27
|
-
|
28
|
-
thread = Thread.new do
|
29
|
-
close_screen
|
30
|
-
|
31
|
-
puts "\nCompose new message to @%s:" % recipient.screen_name
|
32
|
-
|
33
|
-
app.completion_manager.set_default_mode!
|
34
|
-
|
35
|
-
loop do
|
36
|
-
line = (readline('> ', true) || '').strip
|
37
|
-
break if line.empty?
|
38
|
-
|
39
|
-
if line.end_with?('\\')
|
40
|
-
@text << line.chop.rstrip + "\n"
|
41
|
-
else
|
42
|
-
@text << line
|
43
|
-
break
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
puts "\n"
|
48
|
-
|
49
|
-
resetter.call
|
50
|
-
send(recipient) unless text.empty?
|
51
|
-
end
|
52
|
-
|
53
|
-
app.register_interruption_handler do
|
54
|
-
thread.kill
|
55
|
-
clear
|
56
|
-
puts "\nCanceled"
|
57
|
-
resetter.call
|
58
|
-
end
|
59
|
-
|
60
|
-
thread.join
|
61
|
-
end
|
62
|
-
|
63
|
-
private
|
64
|
-
|
65
|
-
attr_reader :app, :client
|
66
|
-
|
67
|
-
def clear
|
68
|
-
@text = ''
|
69
|
-
end
|
70
|
-
|
71
|
-
def send(recipient)
|
72
|
-
client.create_direct_message(recipient, text)
|
73
|
-
clear
|
74
|
-
end
|
75
|
-
|
76
|
-
def text
|
77
|
-
@text || ''
|
78
|
-
end
|
79
|
-
end
|
80
|
-
end
|
@@ -1,51 +0,0 @@
|
|
1
|
-
require 'twterm/client'
|
2
|
-
require 'twterm/direct_message'
|
3
|
-
require 'twterm/event/direct_message/fetched'
|
4
|
-
require 'twterm/publisher'
|
5
|
-
require 'twterm/scheduler'
|
6
|
-
require 'twterm/user'
|
7
|
-
require 'twterm/utils'
|
8
|
-
|
9
|
-
module Twterm
|
10
|
-
class DirectMessageManager
|
11
|
-
include Publisher, Utils
|
12
|
-
|
13
|
-
def initialize(client)
|
14
|
-
check_type Client, client
|
15
|
-
|
16
|
-
@client = client
|
17
|
-
@conversations = {}
|
18
|
-
|
19
|
-
fetch
|
20
|
-
|
21
|
-
Scheduler.new(300) { fetch }
|
22
|
-
end
|
23
|
-
|
24
|
-
def add(collocutor_id, message)
|
25
|
-
check_type DirectMessage, message
|
26
|
-
|
27
|
-
@conversations[collocutor_id] ||= DirectMessage::Conversation.new(collocutor_id)
|
28
|
-
@conversations[collocutor_id] << message
|
29
|
-
end
|
30
|
-
|
31
|
-
def fetch
|
32
|
-
client.direct_messages_received.then do |messages|
|
33
|
-
messages.each { |m| add(m.sender_id, m) }
|
34
|
-
publish(Event::DirectMessage::Fetched.new)
|
35
|
-
end
|
36
|
-
|
37
|
-
client.direct_messages_sent.then do |messages|
|
38
|
-
messages.each { |m| add(m.recipient_id, m) }
|
39
|
-
publish(Event::DirectMessage::Fetched.new)
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
def conversations
|
44
|
-
@conversations.values
|
45
|
-
end
|
46
|
-
|
47
|
-
private
|
48
|
-
|
49
|
-
attr_reader :client
|
50
|
-
end
|
51
|
-
end
|
@@ -1,30 +0,0 @@
|
|
1
|
-
require 'twterm/event/notification/abstract_notification'
|
2
|
-
|
3
|
-
module Twterm
|
4
|
-
module Event
|
5
|
-
module Notification
|
6
|
-
class DirectMessage < AbstractNotification
|
7
|
-
# @param [Twterm::DirectMessage] message
|
8
|
-
# @param [Twterm::User] user
|
9
|
-
def initialize(message, user)
|
10
|
-
@message = message
|
11
|
-
@user = user
|
12
|
-
end
|
13
|
-
|
14
|
-
# @return [String] notification body
|
15
|
-
def body
|
16
|
-
message.text
|
17
|
-
end
|
18
|
-
|
19
|
-
# @return [String] notification title
|
20
|
-
def title
|
21
|
-
"@#{user.screen_name} has sent you a message"
|
22
|
-
end
|
23
|
-
|
24
|
-
private
|
25
|
-
|
26
|
-
attr_reader :message, :user
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|
@@ -1,14 +0,0 @@
|
|
1
|
-
require 'twterm/direct_message'
|
2
|
-
require 'twterm/repository/abstract_entity_repository'
|
3
|
-
|
4
|
-
module Twterm
|
5
|
-
module Repository
|
6
|
-
class DirectMessageRepository < AbstractEntityRepository
|
7
|
-
private
|
8
|
-
|
9
|
-
def type
|
10
|
-
DirectMessage
|
11
|
-
end
|
12
|
-
end
|
13
|
-
end
|
14
|
-
end
|
@@ -1,104 +0,0 @@
|
|
1
|
-
require 'twterm/direct_message_composer'
|
2
|
-
require 'twterm/event/direct_message/fetched'
|
3
|
-
require 'twterm/image_builder/user_name_image_builder'
|
4
|
-
require 'twterm/subscriber'
|
5
|
-
require 'twterm/tab/abstract_tab'
|
6
|
-
require 'twterm/tab/loadable'
|
7
|
-
require 'twterm/tab/searchable'
|
8
|
-
|
9
|
-
module Twterm
|
10
|
-
module Tab
|
11
|
-
module DirectMessage
|
12
|
-
class Conversation < AbstractTab
|
13
|
-
include Searchable
|
14
|
-
include Subscriber
|
15
|
-
include Loadable
|
16
|
-
|
17
|
-
def drawable_item_count
|
18
|
-
messages.drop(scroller.offset).lazy
|
19
|
-
.map { |m| m.text.split_by_width(window.maxx - 4).count + 2 }
|
20
|
-
.scan(0, :+)
|
21
|
-
.each_cons(2)
|
22
|
-
.select { |_, l| l < window.maxy }
|
23
|
-
.count
|
24
|
-
end
|
25
|
-
|
26
|
-
def image
|
27
|
-
return Image.string(initially_loaded? ? 'No results found' : 'Loading...') if items.empty?
|
28
|
-
|
29
|
-
scroller.drawable_items.map.with_index(0) do |message, i|
|
30
|
-
sender = app.user_repository.find(message.sender_id)
|
31
|
-
|
32
|
-
header = [
|
33
|
-
ImageBuilder::UserNameImageBuilder.new(sender).build,
|
34
|
-
Image.string(message.date.to_s).brackets,
|
35
|
-
].intersperse(Image.whitespace).reduce(Image.empty, :-)
|
36
|
-
|
37
|
-
body = message.text.split_by_width(window.maxx - 4)
|
38
|
-
.map { |x| Image.string(x) }
|
39
|
-
.reduce(Image.empty, :|)
|
40
|
-
|
41
|
-
m = header | body
|
42
|
-
|
43
|
-
cursor = Image.cursor(m.height, scroller.current_index?(i))
|
44
|
-
|
45
|
-
cursor - Image.whitespace - m
|
46
|
-
end
|
47
|
-
.intersperse(Image.blank_line)
|
48
|
-
.reduce(Image.empty, :|)
|
49
|
-
end
|
50
|
-
|
51
|
-
def initialize(app, client, conversation)
|
52
|
-
super(app, client)
|
53
|
-
|
54
|
-
@conversation = conversation
|
55
|
-
|
56
|
-
subscribe(Event::DirectMessage::Fetched) { initially_loaded! }
|
57
|
-
end
|
58
|
-
|
59
|
-
def items
|
60
|
-
messages
|
61
|
-
end
|
62
|
-
|
63
|
-
def matches?(message, query)
|
64
|
-
sender = app.user_repository.find(message.sender_id)
|
65
|
-
|
66
|
-
[
|
67
|
-
message.text,
|
68
|
-
sender.screen_name,
|
69
|
-
sender.name
|
70
|
-
].any? { |x| x.downcase.include?(query.downcase) }
|
71
|
-
end
|
72
|
-
|
73
|
-
def respond_to_key(key)
|
74
|
-
return true if scroller.respond_to_key(key)
|
75
|
-
|
76
|
-
k = KeyMapper.instance
|
77
|
-
|
78
|
-
case key
|
79
|
-
when k[:status, :compose], k[:status, :reply]
|
80
|
-
collocutor = app.user_repository.find(conversation.collocutor_id)
|
81
|
-
app.direct_message_composer.compose(collocutor)
|
82
|
-
else
|
83
|
-
return false
|
84
|
-
end
|
85
|
-
|
86
|
-
true
|
87
|
-
end
|
88
|
-
|
89
|
-
def title
|
90
|
-
collocutor = app.user_repository.find(conversation.collocutor_id)
|
91
|
-
'@%s messages' % collocutor.screen_name
|
92
|
-
end
|
93
|
-
|
94
|
-
private
|
95
|
-
|
96
|
-
attr_reader :conversation
|
97
|
-
|
98
|
-
def messages
|
99
|
-
@conversation.messages
|
100
|
-
end
|
101
|
-
end
|
102
|
-
end
|
103
|
-
end
|
104
|
-
end
|
@@ -1,100 +0,0 @@
|
|
1
|
-
require 'twterm/direct_message_composer'
|
2
|
-
require 'twterm/event/direct_message/fetched'
|
3
|
-
require 'twterm/image_builder/user_name_image_builder'
|
4
|
-
require 'twterm/subscriber'
|
5
|
-
require 'twterm/tab/abstract_tab'
|
6
|
-
require 'twterm/tab/loadable'
|
7
|
-
require 'twterm/tab/direct_message/conversation'
|
8
|
-
|
9
|
-
module Twterm
|
10
|
-
module Tab
|
11
|
-
module DirectMessage
|
12
|
-
class ConversationList < AbstractTab
|
13
|
-
include Loadable
|
14
|
-
include Searchable
|
15
|
-
include Subscriber
|
16
|
-
|
17
|
-
def drawable_item_count
|
18
|
-
window.maxy.-(2).div(3)
|
19
|
-
end
|
20
|
-
|
21
|
-
def image
|
22
|
-
return Image.string(initially_loaded? ? 'No results found' : 'Loading...') if items.empty?
|
23
|
-
|
24
|
-
scroller.drawable_items.map.with_index(0) do |conversation, i|
|
25
|
-
cursor = Image.cursor(2, scroller.current_index?(i))
|
26
|
-
|
27
|
-
collocutor = app.user_repository.find(conversation.collocutor_id)
|
28
|
-
|
29
|
-
header = [
|
30
|
-
ImageBuilder::UserNameImageBuilder.new(collocutor).build,
|
31
|
-
Image.string(conversation.updated_at.to_s).brackets,
|
32
|
-
].intersperse(Image.whitespace).reduce(Image.empty, :-)
|
33
|
-
|
34
|
-
body = Image.string(conversation.preview.split_by_width(window.maxx - 4).first)
|
35
|
-
|
36
|
-
cursor - Image.whitespace - (header | body)
|
37
|
-
end
|
38
|
-
.intersperse(Image.blank_line)
|
39
|
-
.reduce(Image.empty, :|)
|
40
|
-
end
|
41
|
-
|
42
|
-
def initialize(app, client)
|
43
|
-
super(app, client)
|
44
|
-
|
45
|
-
subscribe(Event::DirectMessage::Fetched) { initially_loaded! }
|
46
|
-
end
|
47
|
-
|
48
|
-
def ==(other)
|
49
|
-
other.is_a?(self.class)
|
50
|
-
end
|
51
|
-
|
52
|
-
def items
|
53
|
-
client.direct_message_conversations
|
54
|
-
end
|
55
|
-
|
56
|
-
def matches?(conversation, query)
|
57
|
-
collocutor = app.user_repository.find(conversation.collocutor_id)
|
58
|
-
|
59
|
-
[
|
60
|
-
collocutor.name,
|
61
|
-
collocutor.screen_name,
|
62
|
-
conversation.preview
|
63
|
-
].any? { |x| x.downcase.include?(query.downcase) }
|
64
|
-
end
|
65
|
-
|
66
|
-
def respond_to_key(key)
|
67
|
-
return true if scroller.respond_to_key(key)
|
68
|
-
|
69
|
-
k = KeyMapper.instance
|
70
|
-
|
71
|
-
case key
|
72
|
-
when 10
|
73
|
-
open_conversation
|
74
|
-
when k[:status, :compose], k[:status, :reply]
|
75
|
-
conversation = current_item
|
76
|
-
collocutor = app.user_repository.find(conversation.collocutor_id)
|
77
|
-
app.direct_message_composer.compose(collocutor)
|
78
|
-
else
|
79
|
-
return false
|
80
|
-
end
|
81
|
-
|
82
|
-
true
|
83
|
-
end
|
84
|
-
|
85
|
-
def title
|
86
|
-
'Direct Messages'
|
87
|
-
end
|
88
|
-
|
89
|
-
private
|
90
|
-
|
91
|
-
def open_conversation
|
92
|
-
conversation = scroller.current_item
|
93
|
-
|
94
|
-
tab = Tab::DirectMessage::Conversation.new(app, client, conversation)
|
95
|
-
app.tab_manager.add_and_show(tab)
|
96
|
-
end
|
97
|
-
end
|
98
|
-
end
|
99
|
-
end
|
100
|
-
end
|