git » sdk » main » tree

[main] / borogove / MemberUpdate.hx

package borogove;

import thenshim.Promise;
import haxe.ds.ReadOnlyArray;

import borogove.Chat;
import borogove.queries.PubsubGet;

#if cpp
import HaxeCBridge;
#end

// Some updates are partial
// We encode the more low-level values here
@:nullSafety(Strict)
class MemberUpdate {
	public final id: Null<String>;
	public final jid: Null<JID>;
	private final displayName: Null<String>;
	private final isSelf: Bool;
	private final affiliation: Null<Role>; // Null means "member"
	private final presence: Map<String, Presence>;

	@:allow(borogove)
	private function new(id: Null<String>, jid: Null<JID>, displayName: Null<String>, isSelf: Bool, affiliation: Null<Role>, presence: Map<String, Presence>) {
		this.id = id;
		this.jid = jid;
		this.displayName = displayName;
		this.isSelf = isSelf;
		this.affiliation = affiliation;
		this.presence = presence;
	}

	public function applyTo(member: Null<Member>) {
		if (id != null || jid != null) {
			if (member != null && member?.id != id && member?.chat?.chatId != jid?.asString()) throw "Member does not match this update";
		}
		if (member != null && isSelf != member?.isSelf) throw "Member does not match this update";
		final filteredRoles = affiliation?.id == "none" ? [] : (member?.roles ?? []).filter(r -> !["owner", "admin", "member", "none", "outcast"].contains(r.id));
		final mergedPresence = new Map();

		for (resource => p in member?.presence ?? new Map()) {
			mergedPresence.set(resource, p);
		}

		for (resource => p in presence) {
			mergedPresence.set(resource, p);
		}

		return {
			id: id ?? member?.id,
			displayName: displayName ?? (member?.displayName == "" ? null : member?.displayName) ?? jid?.asString(),
			photoUri: member?.photoUri,
			isSelf: isSelf,
			roles: filteredRoles.concat(affiliation == null ? [] : [affiliation]),
			jid: member?.jid ?? jid,
			presence: mergedPresence,
			chat: jid == null ? null : new AvailableChat(jid.asBare().asString(), displayName, "", CapsRepo.empty)
		};
	}

	static public function extractUpdates(accountId: String, chat: Chat, stanza: Stanza) {
		final updates = [];
		final mucUser = stanza.getChild("x", "http://jabber.org/protocol/muc#user");
		if (mucUser != null) {
			final sOccupantId = stanza.getChild("occupant-id", "urn:xmpp:occupant-id:0")?.attr?.get("id");
			final from = stanza.attr.get("from");
			final resource = stanza.name == "presence" && from != null ? JID.parse(from).resource : null;
			final channel = Util.downcast(chat, Channel);
			for (item in mucUser.allTags("item")) {
				final jidS = item.attr.get("jid");
				final jid = jidS == null ? null : JID.parse(jidS).asBare();
				final occupantId: Null<String> = item.getChild("occupant-id", "urn:xmpp:occupant-id:0")?.attr?.get("id") ?? sOccupantId;
				final id = occupantId == null ? null : chat.chatId + "/" + occupantId;
				final aff = item.attr.get("affiliation");
				if (aff == null) {
					trace("No affiliation on affiliation update", stanza);
				} else {
					updates.push(new MemberUpdate(
						id,
						jid,
						item.attr.get("nick") ?? resource,
						jid?.asString() == accountId || channel?.self?.id == id,
						Role.forAffiliation(aff),
						stanza.name == "presence" ? [ resource ?? "" => stanza ] : new Map()
					));
				}
			}
		}

		return updates;
	}
}