@@ 26,13 26,20 @@ if DB.user_version < 1
DB.user_version = 1
end
+if DB.user_version < 2
+ DB.execute(<<~SQL)
+ ALTER TABLE messages ADD COLUMN delivered INTEGER NOT NULL DEFAULT 0
+ SQL
+ DB.user_version = 2
+end
+
BLATHER = self
include Glimmer
set_caps(
"https://git.singpolyma.net/jabber-client-demo",
[],
- ["urn:xmpp:avatar:metadata+notify"]
+ ["urn:xmpp:avatar:metadata+notify", "urn:xmpp:receipts"]
)
$avatars = {}
@@ 56,11 63,11 @@ class Conversation
def initialize(jid)
@jid = jid
- @messages = [[default_avatar(""), "", ""]]
+ @messages = [[default_avatar(""), "", "", false, nil]]
EM.defer do
mam_messages = []
DB.execute(<<~SQL, [@jid]) do |row|
- SELECT stanza
+ SELECT stanza,delivered
FROM messages
WHERE conversation=?
ORDER BY created_at
@@ 68,11 75,11 @@ class Conversation
m = Blather::XMPPNode.import(
Nokogiri::XML.parse(row[0]).root
)
- mam_messages << m
+ mam_messages << [m, row[1]]
end
LibUI.queue_main do
- mam_messages.map! { |m| message_row(m) }
+ mam_messages.map! { |args| message_row(*args) }
@messages.replace(mam_messages + @messages)
end
end
@@ 99,9 106,10 @@ class Conversation
image_column("Avatar")
text_column("Sender")
text_column("Message")
+ checkbox_column("Delivered")
editable false
cell_rows @messages
- @messages.clear if @messages.length == 1 && @messages.first.last == ""
+ @messages.clear if @messages.length == 1 && @messages.first.last.nil?
}
horizontal_box {
@@ 113,6 121,7 @@ class Conversation
on_clicked do
m = message
+ xml_child(m, :request, "urn:xmpp:receipts")
EM.defer do
BLATHER << m
DB.execute(<<~SQL, [nil, m.id, @jid, m.to_s])
@@ 121,7 130,7 @@ class Conversation
VALUES (?,?,?,unixepoch(),?)
SQL
end
- @messages << message_row(m)
+ @messages << message_row(m, false)
@message_entry.text = ""
end
}
@@ 153,17 162,26 @@ class Conversation
image { image_part(rgba * 32 * 32, 32, 32, 4) }
end
- def message_row(m)
+ def message_row(m, delivered)
from = m.from&.stripped || BLATHER.jid.stripped
[
$avatars[from.to_s] || default_avatar(from.to_s),
format_sender(from),
- m.body
+ m.body,
+ delivered,
+ m.id
]
end
def new_message(m)
- @messages << message_row(m)
+ @messages << message_row(m, true)
+ end
+
+ def delivered_message(id)
+ row = @messages.find_index { |r| r.last == id }
+ return unless row
+
+ @messages[row] = @messages[row][0..-3] + [true, id]
end
end
@@ 271,7 289,7 @@ def handle_live_message(m, counterpart: m.from.stripped.to_s)
}&.[]("id")
delay = m.delay&.stamp&.to_i || Time.now.to_i
DB.execute(<<~SQL, [mam_id, m.id, counterpart, delay, m.to_s])
- INSERT INTO messages (mam_id, stanza_id, conversation, created_at, stanza) VALUES (?,?,?,?,?)
+ INSERT INTO messages (mam_id, stanza_id, conversation, created_at, stanza, delivered) VALUES (?,?,?,?,?,1)
SQL
if mam_id
@@ 365,6 383,14 @@ end
message :body do |m|
handle_live_message(m)
+
+ if m.id && m.at("./ns:request", ns: "urn:xmpp:receipts")
+ self << m.reply(remove_children: true).tap { |receipt|
+ xml_child(receipt, :received, "urn:xmpp:receipts").tap { |received|
+ received["id"] = m.id
+ }
+ }
+ end
end
pubsub_event(
@@ 394,6 420,19 @@ pubsub_event(
end
end
+message "./ns:received", ns: "urn:xmpp:receipts" do |m, received|
+ DB.execute(<<~SQL, [received.first["id"].to_s])
+ UPDATE messages SET delivered=1 WHERE stanza_id=?
+ SQL
+
+ conversation = $conversations[m.from.stripped.to_s]
+ return unless conversation
+
+ LibUI.queue_main do
+ conversation.delivered_message(received.first["id"].to_s)
+ end
+end
+
after(:roster) do
LibUI.queue_main do
$roster.clear