2using System.Collections.Generic;
4using System.Threading.Tasks;
6using System.Windows.Input;
7using System.Windows.Media;
25 private readonly Dictionary<string, RoomNode> roomByJid =
new Dictionary<string, RoomNode>();
27 private bool handlersAdded;
31 : base(
Parent, JID, Name, Node, Features)
33 this.mucClient = MucClient;
35 this.children =
new SortedDictionary<string, TreeNode>()
37 {
string.Empty,
new Loading(
this) }
40 this.mucClient.OccupantPresence += this.MucClient_OccupantPresence;
41 this.mucClient.OccupantRequest += this.MucClient_OccupantRequest;
42 this.mucClient.PrivateMessageReceived += this.MucClient_PrivateMessageReceived;
43 this.mucClient.RegistrationRequest += this.MucClient_RegistrationRequest;
44 this.mucClient.RoomDeclinedInvitationReceived += this.MucClient_RoomDeclinedInvitationReceived;
45 this.mucClient.RoomDestroyed += this.MucClient_RoomDestroyed;
46 this.mucClient.RoomInvitationReceived += this.MucClient_RoomInvitationReceived;
47 this.mucClient.RoomMessage += this.MucClient_RoomMessage;
48 this.mucClient.RoomOccupantMessage += this.MucClient_RoomOccupantMessage;
49 this.mucClient.RoomPresence += this.MucClient_RoomPresence;
50 this.mucClient.RoomSubject += this.MucClient_RoomSubject;
51 this.handlersAdded =
true;
64 return base.RemoveChild(Node);
69 this.UnregisterHandlers();
74 this.UnregisterHandlers();
78 private void UnregisterHandlers()
80 if (this.handlersAdded)
82 this.mucClient.OccupantPresence -= this.MucClient_OccupantPresence;
83 this.mucClient.OccupantRequest -= this.MucClient_OccupantRequest;
84 this.mucClient.PrivateMessageReceived -= this.MucClient_PrivateMessageReceived;
85 this.mucClient.RegistrationRequest -= this.MucClient_RegistrationRequest;
86 this.mucClient.RoomDeclinedInvitationReceived -= this.MucClient_RoomDeclinedInvitationReceived;
87 this.mucClient.RoomDestroyed -= this.MucClient_RoomDestroyed;
88 this.mucClient.RoomInvitationReceived -= this.MucClient_RoomInvitationReceived;
89 this.mucClient.RoomMessage -= this.MucClient_RoomMessage;
90 this.mucClient.RoomOccupantMessage -= this.MucClient_RoomOccupantMessage;
91 this.mucClient.RoomPresence -= this.MucClient_RoomPresence;
92 this.mucClient.RoomSubject -= this.MucClient_RoomSubject;
93 this.handlersAdded =
false;
99 public override ImageSource ImageResource => XmppAccountNode.chatBubble;
100 public override bool CanRecycle =>
true;
104 if (!(this.children is
null))
106 foreach (
TreeNode Node
in this.children.Values)
114 public override string ToolTip =>
"Multi-User Chat Service";
116 private bool loadingChildren =
false;
120 if (!this.loadingChildren && !this.IsLoaded)
122 Mouse.OverrideCursor = Cursors.Wait;
124 this.loadingChildren =
true;
127 this.loadingChildren =
false;
132 SortedDictionary<string, TreeNode> Children = new SortedDictionary<string, TreeNode>();
134 this.NodesRemoved(this.children.Values, this);
136 lock (this.roomByJid)
138 foreach (KeyValuePair<string, RoomNode> P in this.roomByJid)
139 Children[P.Key] = P.Value;
146 int i = Item.JID.IndexOf(
'@');
150 RoomId = string.Empty;
155 RoomId = Item.JID.Substring(0, i);
156 Domain = Item.JID.Substring(i + 1);
159 string Jid = RoomId +
"@" + Domain;
161 string Prefix =
this.mucClient.Client.BareJID +
"." + Jid;
165 lock (this.roomByJid)
167 if (!this.roomByJid.TryGetValue(Jid, out
RoomNode Node))
169 Node = new RoomNode(this, RoomId, Domain, NickName, Password, Item.Name, false);
170 this.roomByJid[Jid] = Node;
171 Children[Item.JID] = Node;
176 this.children =
new SortedDictionary<string, TreeNode>(
Children);
178 this.NodesAdded(
Children.Values,
this);
181 MainWindow.ErrorBox(
string.IsNullOrEmpty(e.ErrorText) ?
"Unable to get rooms." : e.ErrorText);
189 public override bool CanAddChildren =>
true;
191 public override void Add()
195 Owner = MainWindow.currentInstance
198 bool? Result = Dialog.ShowDialog();
200 if (Result.HasValue && Result.Value)
202 string RoomId = Dialog.RoomID.Text;
203 string Domain = Dialog.Domain.Text;
204 string NickName = Dialog.NickName.Text;
205 string Password = Dialog.Password.Password;
206 string Prefix = this.mucClient.Client.BareJID +
"." + RoomId +
"@" + Domain;
209 this.mucClient.EnterRoom(RoomId, Domain, NickName, Password, async (Sender, e) =>
213 if (e.HasStatus(MucStatus.Created))
215 await this.mucClient.GetRoomConfiguration(RoomId, Domain, (sender2, e2) =>
221 if (!string.IsNullOrEmpty(Password))
223 Field PasswordProtectionField = Form[
"muc#roomconfig_passwordprotectedroom"];
224 PasswordProtectionField?.SetValue(
"true");
226 Field PasswordField = Form[
"muc#roomconfig_roomsecret"];
227 PasswordField?.SetValue(Password);
230 MainWindow.currentInstance.ShowDataForm(e2.Form);
233 MainWindow.ErrorBox(e2.ErrorText);
235 return Task.CompletedTask;
236 }, async (sender2, e2) =>
240 await RuntimeSettings.SetAsync(Prefix +
".Nick", NickName);
241 await RuntimeSettings.SetAsync(Prefix +
".Pwd", Password);
243 Field NameField = Form[
"muc#roomconfig_roomname"];
244 string Name = NameField?.ValueString ?? RoomId;
246 this.AddRoomNode(RoomId, Domain, NickName, Password, Name, true);
252 await RuntimeSettings.SetAsync(Prefix +
".Nick", NickName);
253 await RuntimeSettings.SetAsync(Prefix +
".Pwd", Password);
255 this.AddRoomNode(RoomId, Domain, NickName, Password, string.Empty, true);
257 await this.MucClient_OccupantPresence(this, e);
261 MainWindow.ErrorBox(
string.IsNullOrEmpty(e.ErrorText) ?
"Unable to add room." : e.ErrorText);
266 private RoomNode AddRoomNode(
string RoomId,
string Domain,
string NickName,
string Password,
string Name,
bool Entered)
268 RoomNode Node =
new RoomNode(
this, RoomId, Domain, NickName, Password, Name, Entered);
270 lock (this.roomByJid)
272 this.roomByJid[RoomId +
"@" + Domain] = Node;
277 if (this.children is
null)
278 this.children =
new SortedDictionary<string, TreeNode>() { { Node.Key, Node } };
283 this.children[Node.Key] = Node;
289 this.Account?.View?.NodeAdded(
this, Node);
291 foreach (TreeNode Node2
in Node.Children)
292 this.Account?.View?.NodeAdded(Node, Node2);
296 return Task.CompletedTask;
310 InvitationReason = e.
Reason,
312 RoomJid = e.RoomId +
"@" + e.Domain
315 this.mucClient.GetRoomInformation(e.
RoomId, e.
Domain, (sender2, e2) =>
319 foreach (Identity Id in e2.Identities)
321 if (Id.Category ==
"conference" && Id.Type ==
"text")
323 Form.RoomName = Id.Name;
328 Form.MembersOnly = e2.MembersOnly;
329 Form.Moderated = e2.Moderated;
330 Form.NonAnonymous = e2.NonAnonymous;
332 Form.PasswordProtected = e2.PasswordProtected;
333 Form.Persistent = e2.Persistent;
334 Form.Public = e2.Public;
335 Form.SemiAnonymous = e2.SemiAnonymous;
336 Form.Temporary = e2.Temporary;
337 Form.Unmoderated = e2.Unmoderated;
338 Form.Unsecured = e2.Unsecured;
343 this.ShowInvitationForm(Form, e, e2);
344 return Task.CompletedTask;
347 return Task.CompletedTask;
350 return Task.CompletedTask;
355 bool? b = Form.ShowDialog();
361 e.
Accept(Form.NickName.Text, (sender2, e3) =>
365 MainWindow.UpdateGui(() =>
367 MessageBox.Show(MainWindow.currentInstance,
368 string.IsNullOrEmpty(e3.ErrorText) ?
"Unable to process invitation." : e3.ErrorText,
369 "Error", MessageBoxButton.OK, MessageBoxImage.Error);
371 RoomInvitationReceivedForm Form2 = new RoomInvitationReceivedForm()
373 Owner = MainWindow.currentInstance,
374 InviteTo = XmppClient.GetBareJID(e.To),
375 InviteFrom = XmppClient.GetBareJID(e.InviteFrom),
376 InvitationReason = e.Reason,
377 RoomName = Form.RoomName,
378 RoomJid = e.RoomId +
"@" + e.Domain,
379 MembersOnly = e2.MembersOnly,
380 Moderated = e2.Moderated,
381 NonAnonymous = e2.NonAnonymous,
383 PasswordProtected = e2.PasswordProtected,
384 Persistent = e2.Persistent,
386 SemiAnonymous = e2.SemiAnonymous,
387 Temporary = e2.Temporary,
388 Unmoderated = e2.Unmoderated,
389 Unsecured = e2.Unsecured
392 this.ShowInvitationForm(Form2, e, e2);
394 return Task.CompletedTask;
398 return Task.CompletedTask;
408 StringBuilder sb =
new StringBuilder();
410 sb.Append(
"Your invitation sent to ");
412 sb.Append(
" to join the room ");
416 sb.Append(
" was declined.");
418 if (!
string.IsNullOrEmpty(e.
Reason))
420 sb.Append(
" Reason: ");
424 MainWindow.MessageBox(sb.ToString(),
"Invitation declined", MessageBoxButton.OK, MessageBoxImage.Exclamation);
426 return Task.CompletedTask;
429 private async Task<RoomNode> GetRoomNode(
string RoomId,
string Domain)
432 string Jid = RoomId +
"@" + Domain;
434 lock (this.roomByJid)
436 if (this.roomByJid.TryGetValue(Jid, out Result))
440 string Prefix = this.mucClient.Client.BareJID +
"." + Jid;
444 Result = this.AddRoomNode(RoomId, Domain, NickName, Password,
string.Empty,
false);
451 RoomNode RoomNode = await this.GetRoomNode(e.
RoomId, e.
Domain);
453 ChatItemType Type = ChatItemType.Received;
455 if (!
string.IsNullOrEmpty(NickName) && RoomNode.NickName == NickName)
457 bool HasDelay =
false;
459 foreach (XmlNode N
in e.
Message.ChildNodes)
461 if (N is XmlElement E && E.LocalName ==
"delay")
471 Type = ChatItemType.Transmitted;
474 RoomNode.EnterIfNotAlready(
true);
476 MainWindow.ParseChatMessage(e, out
string Message, out
bool IsMarkdown, out DateTime Timestamp);
481 IsMarkdown, Timestamp, Type, RoomNode, RoomNode.Header);
487 RoomNode RoomNode = await this.GetRoomNode(e.
RoomId, e.
Domain);
488 if (!RoomNode.Entered)
494 return Task.CompletedTask;
506 return Task.CompletedTask;
517 return Task.CompletedTask;
522 RoomNode RoomNode = await this.GetRoomNode(e.
RoomId, e.
Domain);
526 if (!OccupantNode.Availability.HasValue || e.
Availability != OccupantNode.Availability.Value)
529 OccupantNode.OnUpdated();
535 switch (OccupantNode.Availability)
538 View.Event(
"Online.", e.
NickName,
string.Empty);
542 View.Event(
"Offline.", e.
NickName,
string.Empty);
546 View.Event(
"Away.", e.
NickName,
string.Empty);
550 View.Event(
"Ready to chat.", e.
NickName,
string.Empty);
554 View.Event(
"Busy.", e.
NickName,
string.Empty);
558 View.Event(
"Away (extended).", e.
NickName,
string.Empty);
564 await this.MucClient_RoomPresence(Sender, e, View);
569 return this.MucClient_RoomPresence(Sender, e,
null);
576 RoomNode RoomNode = await this.GetRoomNode(e.
RoomId, e.
Domain);
593 View.Event(
"This room logs messages.", e.
NickName,
string.Empty);
597 View.Event(
"This room does not log messages.", e.
NickName,
string.Empty);
601 View.Event(
"Nick-name changed.", e.
NickName,
string.Empty);
605 View.Event(
"This room does not anonymous.", e.
NickName,
string.Empty);
609 View.Event(
"This room is semi-anonymous.", e.
NickName,
string.Empty);
613 View.Event(
"This room does anonymous.", e.
NickName,
string.Empty);
617 View.Event(
"All participants in this room have access to the full JID of each other.", e.
NickName,
string.Empty);
621 View.Event(
"This room displays unavailable members.", e.
NickName,
string.Empty);
624 case MucStatus.DoesNotShowUnavailableMembers:
625 View.Event(
"This room hieds unavailable members.", e.
NickName,
string.Empty);
628 case MucStatus.NonPrivacyRelatedConfigurationChange:
629 View.Event(
"A configuration that does not affect privacy changed.", e.
NickName,
string.Empty);
636 View.Event(
"Room created.", e.
NickName,
string.Empty);
640 View.Event(
"Banned from the room.", e.
NickName,
string.Empty);
644 View.Event(
"New room nick-name.", e.
NickName,
string.Empty);
648 View.Event(
"Temporarily kicked from the room.", e.
NickName,
string.Empty);
651 case MucStatus.RemovedDueToAffiliationChange:
652 View.Event(
"Removed from the room due to an affiliation change.", e.
NickName,
string.Empty);
655 case MucStatus.RemovedDueToNonMembership:
656 View.Event(
"Removed from the room, since no longer member.", e.
NickName,
string.Empty);
659 case MucStatus.RemovedDueToSystemShutdown:
660 View.Event(
"Removed from the room due to system shutdown.", e.
NickName,
string.Empty);
664 View.Event(
"Removed from the room due to technical problems.", e.
NickName,
string.Empty);
672 View?.Event(
"Room has been destroyed on the host.", e.
NickName,
string.Empty);
674 RoomNode.Parent.RemoveChild(RoomNode);
675 RoomNode.Parent.OnUpdated();
682 await this.MucClient_RoomPresence(Sender, e);
687 RoomNode RoomNode = await this.GetRoomNode(e.
RoomId, e.
Domain);
688 OccupantNode Occupant = RoomNode.GetOccupantNode(e.
NickName,
null,
null,
null);
690 MainWindow.ParseChatMessage(e, out
string Message, out
bool IsMarkdown, out DateTime Timestamp);
695 IsMarkdown, Timestamp, Occupant, e.
From);
703 MainWindow.ParseChatMessage(e, out
string Message, out
bool IsMarkdown, out DateTime Timestamp);
708 View?.Event(Message,
string.Empty, e.
ThreadID);
Interaction logic for ChatView.xaml
Interaction logic for ParameterDialog.xaml
static async Task< ParameterDialog > CreateAsync(DataForm Form)
Interaction logic for ParameterDialog.xaml
Interaction logic for xaml
Represents a data source in a concentrator.
override bool RemoveChild(TreeNode Node)
Removes a child node.
override async Task Recycle(MainWindow Window)
Is called when the user wants to recycle the node.
override void Dispose()
Disposes of the node and its resources.
override void LoadChildren()
Method is called to make sure children are loaded.
override void Add()
Is called when the user wants to add a node to the current node.
override void Removed(MainWindow Window)
Is called when the node has been removed from the main window.
Represents a room hosted by a Multi-User Chat service.
Abstract base class for tree nodes in the connection view.
TreeNode[] Children
Children of the node. If null, children are not loaded.
abstract bool CanRecycle
If the node can be recycled.
TreeNode Parent
Parent node. May be null if a root node.
virtual Task Recycle(MainWindow Window)
Is called when the user wants to recycle the node.
virtual void OnUpdated()
Raises the Updated event.
Contains a markdown document. This markdown document class supports original markdown,...
static Task< MarkdownDocument > CreateAsync(string MarkdownText, params Type[] TransparentExceptionTypes)
Contains a markdown document. This markdown document class supports original markdown,...
string From
From where the message was received.
XmlElement Message
The message stanza.
string Subject
Human readable subject.
string To
To whom the message was sent.
string ThreadID
Thread ID.
string From
From where the presence was received.
Availability Availability
Resource availability.
string To
To whom the presence was sent.
Client managing communication with a Multi-User-Chat service. https://xmpp.org/extensions/xep-0045....
Message from a MUC room containing a declined invitation.
string DeclinedFrom
JID of entity sending the declined invitation.
string Reason
Reason for declining the invitation.
Message from a MUC room containing an invitation.
Task Decline()
Declines the invitation, and enters the room.
string Reason
Reason for invitation.
Task Accept(string NickName, EventHandlerAsync< UserPresenceEventArgs > Callback, object State)
Accepts the invitation, and enters the room.
string InviteFrom
JID of entity sending the invitation.
string Domain
Domain hosting the room.
Message from a MUC room occupant.
string NickName
Nick-name of occupant.
Event arguments for user presence events.
string FullJid
Full JID, if privileges allow (null otherwise).
bool RoomDestroyed
If room has been destroyed.
string Domain
Domain hosting the room.
Affiliation? Affiliation
User Affiliation in room, if provided.
override string NickName
Nick-Name of user sending presence.
Role? Role
User role in room, if provided.
MucStatus[] MucStatus
Any status codes informing about changes to status.
Contains information about an item of an entity.
Manages an XMPP client connection. Implements XMPP, as defined in https://tools.ietf....
static string GetBareJID(string JID)
Gets the Bare JID from a JID, which may be a Full JID.
static string GetResource(string JID)
Gets the resource part of a full JID. If no resource part is available, the empty string is returned.
Task SendServiceItemsDiscoveryRequest(string To, EventHandlerAsync< ServiceItemsDiscoveryEventArgs > Callback, object State)
Sends a service items discovery request
Static class managing persistent settings.
static async Task< string > GetAsync(string Key, string DefaultValue)
Gets a string-valued setting.
MucStatus
MUC Status, as defined in https://xmpp.org/registrar/mucstatus.html
Availability
Resource availability.
Prefix
SI prefixes. http://physics.nist.gov/cuu/Units/prefixes.html