| author | Stephen Paul Weber
<singpolyma@singpolyma.net> 2024-11-14 04:42:52 UTC |
| committer | Stephen Paul Weber
<singpolyma@singpolyma.net> 2024-11-14 04:42:52 UTC |
| parent | 7adcfb336543271f3cc848cd07acb44b0a78dd27 |
| snikket/Chat.hx | +12 | -2 |
| snikket/Hash.hx | +5 | -0 |
| snikket/Reaction.hx | +4 | -2 |
| snikket/Stanza.hx | +1 | -2 |
| snikket/Util.hx | +5 | -0 |
diff --git a/snikket/Chat.hx b/snikket/Chat.hx index 8e87428..12a8590 100644 --- a/snikket/Chat.hx +++ b/snikket/Chat.hx @@ -15,6 +15,7 @@ import snikket.jingle.Session; import snikket.queries.DiscoInfoGet; import snikket.queries.MAMQuery; using Lambda; +using StringTools; #if cpp import HaxeCBridge; @@ -192,9 +193,18 @@ abstract class Chat { @param m ChatMessage to react to @param reaction emoji of the reaction **/ - public function addReaction(m:ChatMessage, reaction:String) { + public function addReaction(m:ChatMessage, reaction:Reaction) { final toSend = m.reply(); - toSend.text = reaction; + reaction.render( + (text) -> { + toSend.text = text.replace("\u{fe0f}", ""); + return; + }, + (text, uri) -> { + final hash = Hash.fromUri(uri); + toSend.setHtml('<img alt="' + Util.xmlEscape(text) + '" src="' + Util.xmlEscape(hash == null ? uri : hash.bobUri()) + '" />'); + } + ); sendMessage(toSend); } diff --git a/snikket/Hash.hx b/snikket/Hash.hx index 71c1f1a..dbcc0e0 100644 --- a/snikket/Hash.hx +++ b/snikket/Hash.hx @@ -60,6 +60,11 @@ class Hash { } } + @:allow(snikket) + private function bobUri() { + return "cid:" + (algorithm == "sha-1" ? "sha1" : algorithm.urlEncode()) + "+" + toHex() + "@bob.xmpp.org"; + } + @:allow(snikket) private function serializeUri() { return "ni:///" + algorithm.urlEncode() + ";" + toBase64Url(); diff --git a/snikket/Reaction.hx b/snikket/Reaction.hx index be6947a..281be21 100644 --- a/snikket/Reaction.hx +++ b/snikket/Reaction.hx @@ -1,5 +1,7 @@ package snikket; +using StringTools; + @:nullSafety(Strict) @:expose class Reaction { @@ -11,8 +13,8 @@ class Reaction { public function new(senderId: String, timestamp: String, text: String, key: Null<String> = null) { this.senderId = senderId; this.timestamp = timestamp; - this.text = text; - this.key = key ?? text; + this.text = text.replace("\u{fe0f}", ""); + this.key = key ?? this.text; } public function render<T>(forText: (String) -> T, forImage: (String, String) -> T) { diff --git a/snikket/Stanza.hx b/snikket/Stanza.hx index ba95bf6..e7c33ae 100644 --- a/snikket/Stanza.hx +++ b/snikket/Stanza.hx @@ -26,8 +26,7 @@ class TextNode implements NodeInterface { } public function serialize():String { - // NOTE: using STringTools.htmlEscape breaks things if this is one half of a surrogate pair in an adjacent cdata - return StringTools.replace(StringTools.replace(StringTools.replace(content, "&", "&"), "<", "<"), ">", ">"); + return Util.xmlEscape(content); } public function clone():TextNode { diff --git a/snikket/Util.hx b/snikket/Util.hx index 0f76785..15b54ba 100644 --- a/snikket/Util.hx +++ b/snikket/Util.hx @@ -21,6 +21,11 @@ function downcast<T, S>(value: T, c: Class<S>): Null<S> { return cast Std.downcast(cast value, cast c); } +function xmlEscape(s: String) { + // NOTE: using STringTools.htmlEscape breaks things if this is one half of a surrogate pair in an adjacent cdata + return StringTools.replace(StringTools.replace(StringTools.replace(s, "&", "&"), "<", "<"), ">", ">"); +} + macro function getGitVersion():haxe.macro.Expr.ExprOf<String> { #if !display var process = new sys.io.Process('git', ['describe', '--always']);