| author | Stephen Paul Weber
<singpolyma@singpolyma.net> 2023-10-19 02:18:52 UTC |
| committer | Stephen Paul Weber
<singpolyma@singpolyma.net> 2023-10-19 02:18:52 UTC |
| parent | 732ec6a5de3b8a7ccda8b3eb228d7a7ef253746e |
| Makefile | +1 | -1 |
| xmpp/Chat.hx | +51 | -7 |
| xmpp/Client.hx | +13 | -4 |
| xmpp/persistence/browser.js | +2 | -0 |
diff --git a/Makefile b/Makefile index 73573dc..8887228 100644 --- a/Makefile +++ b/Makefile @@ -13,6 +13,6 @@ run-nodejs: test.node.js browser.js: haxe browser.hxml echo "var exports = {};" > browser.js - sed -e 's/hxEnums\["xmpp.EventResult"\] = {/hxEnums["xmpp.EventResult"] = $$hx_exports.xmpp.EventResult = {/' < browser.haxe.js | sed -e 's/hxEnums\["xmpp.MessageDirection"\] = {/hxEnums["xmpp.MessageDirection"] = $$hx_exports.xmpp.MessageDirection = {/' >> browser.js + sed -e 's/hxEnums\["xmpp.EventResult"\] = {/hxEnums["xmpp.EventResult"] = $$hx_exports.xmpp.EventResult = {/' < browser.haxe.js | sed -e 's/hxEnums\["xmpp.MessageDirection"\] = {/hxEnums["xmpp.MessageDirection"] = $$hx_exports.xmpp.MessageDirection = {/' | sed -e 's/hxEnums\["xmpp.UiState"\] = {/hxEnums["xmpp.UiState"] = $$hx_exports.xmpp.UiState = {/' >> browser.js cat xmpp/persistence/*.js >> browser.js echo "export const { xmpp } = exports;" >> browser.js diff --git a/xmpp/Chat.hx b/xmpp/Chat.hx index 0d054ee..a7a598c 100644 --- a/xmpp/Chat.hx +++ b/xmpp/Chat.hx @@ -13,6 +13,12 @@ import xmpp.queries.DiscoInfoGet; import xmpp.queries.MAMQuery; using Lambda; +enum UiState { + Pinned; + Open; // or Unspecified + Closed; // Archived +} + abstract class Chat { private var client:Client; private var stream:GenericStream; @@ -23,12 +29,14 @@ abstract class Chat { public var chatId(default, null):String; public var jingleSessions: Map<String, xmpp.jingle.Session> = []; private var displayName:String; + public var uiState = Open; - public function new(client:Client, stream:GenericStream, persistence:Persistence, chatId:String) { + public function new(client:Client, stream:GenericStream, persistence:Persistence, chatId:String, uiState = Open) { this.client = client; this.stream = stream; this.persistence = persistence; this.chatId = chatId; + this.uiState = uiState; this.displayName = chatId; } @@ -44,6 +52,8 @@ abstract class Chat { abstract public function bookmark():Void; + abstract public function close():Void; + public function getPhoto(callback:(String)->Void) { callback(Color.defaultPhoto(chatId, getDisplayName().charAt(0))); } @@ -216,6 +226,13 @@ class DirectChat extends Chat { ); } + public function close() { + // Should this remove from roster? + uiState = Closed; + persistence.storeChat(client.accountId(), this); + client.trigger("chats/update", [this]); + } + override public function getPhoto(callback:(String)->Void) { if (avatarSha1 != null) { persistence.getMediaUri("sha-1", avatarSha1, (uri) -> { @@ -235,13 +252,24 @@ class DirectChat extends Chat { class Channel extends Chat { private var disco: Caps = new Caps("", [], ["http://jabber.org/protocol/muc"]); // TODO: persist this - public function new(client:Client, stream:GenericStream, persistence:Persistence, chatId:String, ?disco: Caps) { - super(client, stream, persistence, chatId); + public function new(client:Client, stream:GenericStream, persistence:Persistence, chatId:String, uiState = Open, ?disco: Caps) { + super(client, stream, persistence, chatId, uiState); if (disco != null) this.disco = disco; selfPing(disco == null); } public function selfPing(shouldRefreshDisco = true) { + if (uiState == Closed){ + client.sendPresence( + getFullJid().asString(), + (stanza) -> { + stanza.attr.set("type", "unavailable"); + return stanza; + } + ); + return; + } + stream.sendIq( new Stanza("iq", { type: "get", to: getFullJid().asString() }) .tag("ping", { xmlns: "urn:xmpp:ping" }).up(), @@ -348,7 +376,7 @@ class Channel extends Chat { .tag("pubsub", { xmlns: "http://jabber.org/protocol/pubsub" }) .tag("publish", { node: "urn:xmpp:bookmarks:1" }) .tag("item", { id: chatId }) - .tag("conference", { xmlns: "urn:xmpp:bookmarks:1", name: getDisplayName(), autojoin: "true" }) + .tag("conference", { xmlns: "urn:xmpp:bookmarks:1", name: getDisplayName(), autojoin: uiState == Closed ? "false" : "true" }) .textTag("nick", client.displayName()) .up().up() .tag("publish-options") @@ -390,6 +418,14 @@ class Channel extends Chat { } ); } + + public function close() { + uiState = Closed; + persistence.storeChat(client.accountId(), this); + selfPing(false); + bookmark(); // TODO: what if not previously bookmarked? + client.trigger("chats/update", [this]); + } } @:expose @@ -399,22 +435,30 @@ class SerializedChat { public final avatarSha1:Null<BytesData>; public final caps:haxe.DynamicAccess<Caps>; public final displayName:Null<String>; + public final uiState: String; public final klass:String; - public function new(chatId: String, trusted: Bool, avatarSha1: Null<BytesData>, caps: haxe.DynamicAccess<Caps>, displayName: Null<String>, klass: String) { + public function new(chatId: String, trusted: Bool, avatarSha1: Null<BytesData>, caps: haxe.DynamicAccess<Caps>, displayName: Null<String>, uiState: Null<String>, klass: String) { this.chatId = chatId; this.trusted = trusted; this.avatarSha1 = avatarSha1; this.caps = caps; this.displayName = displayName; + this.uiState = uiState ?? "Open"; this.klass = klass; } public function toChat(client: Client, stream: GenericStream, persistence: Persistence) { + final uiStateEnum = switch (uiState) { + case "Pinned": Pinned; + case "Closed": Closed; + default: Open; + } + final chat = if (klass == "DirectChat") { - new DirectChat(client, stream, persistence, chatId); + new DirectChat(client, stream, persistence, chatId, uiStateEnum); } else if (klass == "Channel") { - new Channel(client, stream, persistence, chatId); + new Channel(client, stream, persistence, chatId, uiStateEnum); } else { throw "Unknown class: " + klass; } diff --git a/xmpp/Client.hx b/xmpp/Client.hx index c79de7c..79a04e4 100644 --- a/xmpp/Client.hx +++ b/xmpp/Client.hx @@ -337,17 +337,22 @@ class Client extends xmpp.EventEmitter { /* Return array of chats, sorted by last activity */ public function getChats():Array<Chat> { - return chats; + return chats.filter((chat) -> chat.uiState != Closed); } // We can ask for caps here because presumably they looked this up // via findAvailableChats public function startChat(chatId:String, fn:Null<String>, caps:Caps):Chat { final existingChat = getChat(chatId); - if (existingChat != null) return existingChat; + if (existingChat != null) { + if (existingChat.uiState == Closed) existingChat.uiState = Open; + Std.downcast(existingChat, Channel)?.selfPing(); + this.trigger("chats/update", [existingChat]); + return existingChat; + } final chat = if (caps.isChannel(chatId)) { - final channel = new Channel(this, this.stream, this.persistence, chatId, caps); + final channel = new Channel(this, this.stream, this.persistence, chatId, Open, caps); chats.unshift(channel); channel; } else { @@ -439,11 +444,15 @@ class Client extends xmpp.EventEmitter { } public function chatActivity(chat: Chat) { + if (chat.uiState == Closed) { + chat.uiState = Open; + persistence.storeChat(accountId(), chat); + } var idx = chats.indexOf(chat); if (idx > 0) { chats.splice(idx, 1); chats.unshift(chat); - this.trigger("chats/update", []); + this.trigger("chats/update", [chat]); } } diff --git a/xmpp/persistence/browser.js b/xmpp/persistence/browser.js index 200ca7b..19a86be 100644 --- a/xmpp/persistence/browser.js +++ b/xmpp/persistence/browser.js @@ -83,6 +83,7 @@ exports.xmpp.persistence = { avatarSha1: chat.avatarSha1, caps: chat.caps, displayName: chat.displayName, + uiState: chat.uiState?.toString(), class: chat instanceof xmpp.DirectChat ? "DirectChat" : (chat instanceof xmpp.Channel ? "Channel" : "Chat") }); }, @@ -97,6 +98,7 @@ exports.xmpp.persistence = { r.avatarSha1, r.caps, r.displayName, + r.uiState, r.class )))); },