Introduction
Part 1 – Frameworks
Part 2 – Directory structure
Part 3 – Application and ApplicationFacade
Part 4 – Notifications, Commands & Use Cases
Part 5 – Model & Proxy
Part 6 – The Application View & Mediator
Part 7 – The Login View & Mediator
Part 8 – The Roster View & Mediator
Part 9 – The Chat View & Mediator
Conclusion, Demo & Downloads
The final mediator, and the final actor in our application. The job of the chat view is to popup a window where you receive and send message to another user. You should be able to open multiple chat windows at once, and each window is bound to a specific and unique JID.
I’m just going to present the code for this with a short explanation of anything odd rather than explicitly go through the steps. If you have trouble understanding anything go back to the previous sections on mediators and read them through again.
ChatView.mxml
1: <?xml version="1.0" encoding="utf-8"?>
2: <mx:TitleWindow xmlns:mx="http://www.adobe.com/2006/mxml" title="" showCloseButton="true">
3: <mx:Script>
4: <![CDATA[
5: import mx.formatters.DateFormatter;
6: import org.davekeen.xiffer.events.ChatEvent;
7: import org.jivesoftware.xiff.core.JID;
8: import org.jivesoftware.xiff.data.Message;
9:
10: private var jid:JID;
11:
12: /**
13: * The send button was clicked so dispatch an event to the mediator
14: */
15: private function sendClick():void {
16: var inputText:String = inputTextArea.text;
17:
18: if (inputText && inputText.length > 0) {
19: dispatchEvent(new ChatEvent(ChatEvent.SEND_MESSAGE, jid, inputText));
20: inputTextArea.text = "";
21: }
22: }
23:
24: public function setJID(jid:JID):void {
25: this.jid = jid;
26:
27: title = jid.toBareJID();
28: }
29:
30: public function getJID():JID {
31: return jid;
32: }
33:
34: /**
35: * Format and add a message to the chat pane
36: *
37: * @param message
38: */
39: public function addMessage(message:Message):void {
40: var dateFormatter:DateFormatter = new DateFormatter();
41: dateFormatter.formatString = "HH:NN";
42: chatTextArea.text += "[" + dateFormatter.format(new Date()) + "] " + message.body + "n";
43: }
44:
45: ]]>
46: </mx:Script>
47: <mx:VBox>
48: <mx:TextArea editable="false" width="270" height="200" id="chatTextArea" />
49: <mx:HBox>
50: <mx:TextArea width="210" height="40" id="inputTextArea" />
51: <mx:Button id="sendButton" width="50" height="40" label="Send" click="sendClick()" />
52: </mx:HBox>
53: </mx:VBox>
54: </mx:TitleWindow>
55:
ChatMediator.as
There is something slightly different about this view compared to the Login or Roster views. The chat windows are dynamically created when needed and thrown away when they are closed by the user so we can’t have a permanent view component as the viewComponent of ChatMediator.as. Instead we’ll pass the top level Application.mxml component since this is what’s required in the arguments of PopupManager.addPopup.
1: /*
2: Mediator - PureMVC
3: */
4: package org.davekeen.xiffer.view {
5: import flash.display.DisplayObject;
6: import flash.display.DisplayObjectContainer;
7: import flash.events.Event;
8: import mx.managers.PopUpManager;
9: import mx.managers.PopUpManagerChildList;
10: import org.davekeen.xiffer.ApplicationFacade;
11: import org.davekeen.xiffer.events.ChatEvent;
12: import org.jivesoftware.xiff.core.JID;
13: import org.jivesoftware.xiff.data.Message;
14: import org.puremvc.as3.interfaces.IMediator;
15: import org.puremvc.as3.interfaces.INotification;
16: import org.puremvc.as3.patterns.mediator.Mediator;
17: import org.davekeen.xiffer.view.*;
18: import org.davekeen.xiffer.view.components.ChatView;
19:
20: /**
21: * Chat Mediator - controls and stewards all popup chat windows
22: */
23: public class ChatMediator extends Mediator implements IMediator {
24:
25: // Cannonical name of the Mediator
26: public static const NAME:String = "ChatMediator";
27:
28: /**
29: * An associative array of open ChatViews
30: */
31: private var chatViews:Array;
32:
33: public function ChatMediator(viewComponent:Object) {
34: // pass the viewComponent to the superclass where
35: // it will be stored in the inherited viewComponent property
36: super(NAME, viewComponent);
37:
38: chatViews = new Array();
39: }
40:
41: /**
42: * Get the Mediator name.
43: * <P>
44: * Called by the framework to get the name of this
45: * mediator. If there is only one instance, we may
46: * define it in a constant and return it here. If
47: * there are multiple instances, this method must
48: * return the unique name of this instance.</P>
49: *
50: * @return String the Mediator name
51: */
52: override public function getMediatorName():String {
53: return ChatMediator.NAME;
54: }
55:
56: /**
57: * List all notifications this Mediator is interested in.
58: * <P>
59: * Automatically called by the framework when the mediator
60: * is registered with the view.</P>
61: *
62: * @return Array the list of Nofitication names
63: */
64: override public function listNotificationInterests():Array {
65: return [
66: ApplicationFacade.OPEN_CHAT_WINDOW,
67: ApplicationFacade.RECEIVE_MESSAGE,
68: ApplicationFacade.VALID_LOGIN,
69: ApplicationFacade.DISCONNECT
70: ];
71: }
72:
73: /**
74: * Handle all notifications this Mediator is interested in.
75: * <P>
76: * Called by the framework when a notification is sent that
77: * this mediator expressed an interest in when registered
78: * (see <code>listNotificationInterests</code>.</P>
79: *
80: * @param INotification a notification
81: */
82: override public function handleNotification(note:INotification):void {
83: switch (note.getName()) {
84: case ApplicationFacade.OPEN_CHAT_WINDOW:
85: var jid:JID = note.getBody() as JID;
86: showChatWindow(jid);
87: break;
88: case ApplicationFacade.RECEIVE_MESSAGE:
89: var message:Message = note.getBody() as Message;
90:
91: // Add the message to the view
92: chatViews[message.from.toBareJID()].addMessage(message);
93: break;
94: case ApplicationFacade.VALID_LOGIN:
95: // Enable all chat windows
96: for each (var chatView:ChatView in chatViews)
97: chatView.enabled = true;
98:
99: break;
100: case ApplicationFacade.DISCONNECT:
101: // Disable all chat windows
102: for each (chatView in chatViews)
103: chatView.enabled = false;
104:
105: break;
106: default:
107: break;
108: }
109: }
110:
111: /**
112: * Open up a chat window for this particular JID
113: *
114: * @param jid
115: */
116: private function showChatWindow(jid:JID):void {
117: // If the window exists already don't do anything
118: if (!chatViews[jid.toBareJID()]) {
119: var chatView:ChatView = new ChatView();
120:
121: PopUpManager.addPopUp(chatView, viewComponent as DisplayObjectContainer, false);
122: PopUpManager.bringToFront(chatView);
123: PopUpManager.centerPopUp(chatView);
124:
125: chatView.addEventListener(Event.CLOSE, onChatViewClose);
126: chatView.addEventListener(ChatEvent.SEND_MESSAGE, onSendMessage);
127: chatView.setJID(jid);
128:
129: // Add the chat view to the associative array
130: chatViews[jid.toBareJID()] = chatView;
131: }
132: }
133:
134: /**
135: * The user has typed a message and sent it
136: *
137: * @param chatEvent
138: */
139: private function onSendMessage(chatEvent:ChatEvent):void {
140: var chatView:ChatView = chatEvent.currentTarget as ChatView;
141:
142: // Construct a XIFF message
143: var message:Message = new Message(chatEvent.getJID(), null, chatEvent.getMessage(), null, Message.CHAT_TYPE);
144:
145: // Echo it to our own view
146: chatViews[chatView.getJID().toBareJID()].addMessage(message);
147:
148: // And send off a notification
149: sendNotification(ApplicationFacade.SEND_MESSAGE, message);
150: }
151:
152: /**
153: * The chat window has been closed
154: *
155: * @param event
156: */
157: private function onChatViewClose(event:Event):void {
158: var chatView:ChatView = event.currentTarget as ChatView;
159: chatView.removeEventListener(Event.CLOSE, onChatViewClose);
160: PopUpManager.removePopUp(chatView);
161:
162: // Delete the chat view from the associative array
163: delete chatViews[chatView.getJID().toBareJID()];
164: }
165:
166: }
167: }
SendMessageCommand.as
1: /*
2: Simple Command - PureMVC
3: */
4: package org.davekeen.xiffer.controller {
5: import org.davekeen.xiffer.events.ChatEvent;
6: import org.davekeen.xiffer.model.XMPPProxy;
7: import org.jivesoftware.xiff.data.Message;
8: import org.puremvc.as3.interfaces.INotification;
9: import org.puremvc.as3.patterns.command.SimpleCommand;
10: import org.puremvc.as3.patterns.observer.Notification;
11:
12: /**
13: * Send a message to the proxy
14: */
15: public class SendMessageCommand extends SimpleCommand {
16:
17: override public function execute(note:INotification):void {
18: var message:Message = note.getBody() as Message;
19: var xmppProxy:XMPPProxy = facade.retrieveProxy(XMPPProxy.NAME) as XMPPProxy;
20:
21: // Send the message
22: xmppProxy.sendMessage(message);
23: }
24:
25: }
26: }
And there it is! A working Jabber, Flex and PureMVC application of your very own.
Let’s wrap it all up with a conclusion, a working demo and the full source code.
I was getting a run-time error when sending out the first message in a chat session. I think it was generated by the fact that for the first message in a chat, no chat window was opened on the receiving end. I added this line in the RECEIVE_MESSAGE option of the switch/case block of the ClassMediator.as class, and it solved the issue:
showChatWindow(message.from as JID);
Thanks for the tutorial, very useful!
Hi! I was surfing and found your blog post… nice! I love your blog. 🙂 Cheers! Sandra. R.
大法师