API/FriendManager.js

const { Out } = require("./Out");
const { Dictionary } = require("./Dictionary");
const { SessionAccessLevel } = require("./SessionAccessLevel");
const { Friend } = require("./Friend");
/**
 * @class FriendManager
 */
class FriendManager {
	static get UPDATE_PERIOD_SECONDS() {
		return 5;
	}
	constructor(cloud) {
		this.Cloud = cloud;
		/** @type Dictionary<string, Friend> */
		this.friends = new Dictionary();
		/**
		 * @private
		 * @type Dictionary<string, SessionInfo> */
		this._friendSessions;
		/** @type Date */
		this.lastStatusUpdate = new Date(0);
		this.initialFetch = false;
		/** @type Date */
		this.lastRequest = new Date(0);
		/**
		 * @private
		 * @type boolean */
		this._friendsChanged;
		/** @type CloudXInterface */
		this.Cloud;
		/** @type Number */
		this.FriendRequestCount;
		Object.defineProperties(this, {
			_friendSessions: {
				value: new Dictionary(),
				writable: true,
			},
			_lock: {
				value: new Object(),
				writable: false,
			},
			_friendsChanged: {
				value: new Boolean(),
				writable: true,
			},
		});
	}

	/**.
	 *
	 *
	 * @param {CloudXInterface} cloud
	 * @memberof FriendManager
	 */
	FriendManager(cloud) {
		this.Cloud = cloud;
	}
	/**
	 *
	 * @returns {boolean}
	 * @readonly
	 * @memberof FriendManager
	 */
	get FriendCount() {
		return this.friends.Count;
	}
	/**.
	 *
	 *
	 * @param {List<Friend>} list
	 * @param friendId
	 * @memberof FriendManager
	 */
	GetFriends(friendId) {
		var friend = new Out();
		switch (friendId.constructor.name) {
		case "List":
			for (let friend of this.friends) {
				friendId.Add(friend.Value);
			}
			break;
		case "String":
			if (this.friends.TryGetValue(friendId, friend)) return friend.Out;
			return null;
		}
	}

	/**.
	 *
	 *
	 * @param {Action<Friend>} action
	 * @memberof FriendManager
	 */
	ForeachFriend(action) {
		for (let friend of this.friends) {
			action(friend.Value);
		}
	}
	/**.
	 *
	 *
	 * @param {List<SessionInfo>} sessions
	 *
	 * @memberof FriendManager
	 */
	GetFriendSessions(sessions) {
		for (let friendSession of this._friendSessions) {
			sessions.Add(friendSession.Value);
		}
		return this._friendSessions.Count;
	}
	ForeachFriendSession(action) {
		for (let friendSession of this._friendSessions) {
			action(friendSession.Value);
		}
	}
	/**.
	 *
	 *
	 * @param {string} friendId
	 * @returns {(Friend | Friend<Null>)}
	 * @memberof FriendManager
	 */
	GetFriend(friendId) {
		let friend = new Out();
		if (this.friends.TryGetValue(friendId, friend)) return friend.Out;
		return null;
	}
	FindFriend(predicate) {
		for (let friend of this.friends) {
			if (predicate(friend.Value)) return friend.Value;
		}
		return null;
	}
	IsFriend(userId) {
		let friend = new Out();
		if (this.friends.TryGetValue(userId, friend))
			return friend.Out.FriendStatus === "Accepted";
		return false;
	}

	/**.
	 *	Get the number of friends in a session
	 *
	 * @param {SessionInfo} session
	 * @returns {Number} Friends in Session
	 * @memberof FriendManager
	 */
	CountPresentFriends(session) {
		if (session.SessionUsers == null || session.SessionUsers.length === 0)
			return 0;
		var num = 0;
		for (let sessionUser of session.SessionUsers) {
			if (
				sessionUser.IsPresent &&
				sessionUser.UserId != null &&
				this.friends.ContainsKey(sessionUser.UserId)
			)
				num++;
		}
		return num;
	}

	/**
	 *
	 * @param {(string | Friend)} friend
	 * @memberof FriendManager
	 */
	AddFriend(friend) {
		switch (friend.constructor.name) {
		case "String":
			this.AddFriend(
				new Friend({
					id: friend,
					friendUsername: friend.substr(2),
					friendStatus: "Accepted",
				})
			);
			break;
		case "Friend":
			friend.OwnerId = this.Cloud.CurrentUser.Id;
			friend.FriendStatus = "Accepted";
			this.Cloud.UpsertFriend(friend);
			this.AddedOrUpdated(friend);
			break;
		}
	}
	RemoveFriend(friend) {
		friend.OwnerId = this.Cloud.CurrentUser.Id;
		friend.FriendStatus = "Ignored";
		this.Cloud.DeleteFriend(friend);
		this.Removed(friend);
	}
	IgnoreRequest(friend) {
		friend.OwnerId = this.Cloud.CurrentSession.UserId;
		friend.FriendStatus = "Ignored";
		this.Cloud.UpsertFriend(friend);
		this.AddedOrUpdated(friend);
	}
	/**.
	 *
	 *
	 * @param {Friend} friend
	 * @memberof FriendManager
	 */
	AddedOrUpdated(friend) {
		let old = new Out();
		if (!this.friends.TryGetValue(friend.FriendUserId, old)) {
			this.friends.Add(friend.FriendUserId, friend);
			let friendAdded = this.FriendAdded;
			if (friendAdded != null) {
				friendAdded(friend);
			}
			this._friendsChanged = true;
		} else {
			if (!friend.IsSame(old.Out)) {
				this.friends.Replace(friend.FriendUserId, friend);
				let friendUpdated = this.FriendUpdated;
				if (friendUpdated != null) friendUpdated(friend, old.Out);
				this._friendsChanged = true;
			}
		}
	}
	/**.
	 *
	 *
	 * @param {Friend} friend
	 * @memberof FriendManager
	 */
	Removed(friend) {
		this.friends.Remove(friend.FriendUserId);
		let friendRemoved = this.FriendRemoved;
		if (friendRemoved != null) friendRemoved(friend);
		this._friendsChanged = true;
	}
	Reset() {
		for (let friend of this.friends) {
			let friendRemoved = this.FriendRemoved;
			if (friendRemoved != null) {
				friendRemoved(friend.Value);
			}
		}
		this.friends.Clear();
		this.lastStatusUpdate = new Date(0);
		this.lastRequest = new Date(0);
	}
	Update() {
		if (this._friendsChanged) {
			this._friendsChanged = false;
			let num;
			num = this.friends.filter((f) => {
				if (f.Value.FriendStatus === "Requested")
					return f.Value.FriendUserId !== this.Cloud.CurrentUser.Id;
				return false;
			}).length;
			this._friendSessions.Clear();
			for (let friend of this.friends) {
				if (!friend.Value.UserStatus) friend.Value.UserStatus = {};
				if (friend.Value.UserStatus.ActiveSessions != null) {
					for (let activeSession in friend.Value.UserStatus.ActiveSessions) {
						if (
							activeSession.AccessLevel === SessionAccessLevel.Friends &&
							!this._friendSessions.ContainsKey(activeSession.SessionId)
						)
							this._friendSessions.Add(activeSession.SessionId, activeSession);
					}
				}
			}
			if (num !== this.FriendRequestCount) {
				this.FriendRequestCount = num;
				let requestCountChanged = this.FriendRequestCountChanged;
				if (requestCountChanged != null)
					requestCountChanged(this.FriendRequestCount);
			}
			let friendsChanged = this.FriendsChanged;
			if (friendsChanged != null) friendsChanged();
		}
		if (
			this.Cloud.CurrentUser == null ||
			new Date(new Date() - this.lastRequest).getTime() <
				FriendManager.UPDATE_PERIOD_SECONDS * 1000
		) {
			return;
		}
		this.lastRequest = new Date();
		if (!this.initialFetch) {
			this.lastStatusUpdate = null;
			this.initialFetch = true;
		}
		this.Cloud.GetFriends(this.lastStatusUpdate).then((friends) => {
			if (friends.IsError) {
				return;
			}
			for (let friend of friends.Entity) {
				if (friend.UserStatus != null) {
					this.lastStatusUpdate = new Date();
				}
				this.AddedOrUpdated(friend);
			}
		});
	}
}
module.exports = {
	FriendManager,
};