Soashable Web Application - Application Design

Application Design

The application design isn't very thought out at this point. There are a few dialogs and some reusable components, but no framework for creating and registering new windows, packet filters, etc. It's all ad-hoc. There are a few coherent bits, and they're described here.

Configuration

This application should be designed in such a way that it is able to discover compatible services on the remote network with zero configuration. In practicality, this requires a lot of overhead for each client that comes to use the site, and will discover the same information.

Currently transports are hardcoded by host name conventions of Openfire. I see four approaches that should all be available to a client: 1) hardcode 2) discover everything 3) discover at compile time 4) have a custom "soashable extensions" component that aggregates configuration in one shot for the client.

  • Transport Logins

    Transports should be just another compatible service on a network (see above). The way Soashable (XEP-100 Gateway Interaction in general) is designed is as as follows:

     XMPP (username or anonymous @soashable.com)
      |
      +- AIM Transport
      |
      +- Yahoo! Transport
      |
      +- MSN Transport
      |
      +- Other XMPP (GTalk, etc)
    

    No Soashable account is required to use transports on the network (depending on the network's configuration, anyway).

    There is currently no way to connect to multiple accounts on a single transport because of shortcomings in XEP-100. This is a major hurdle that can be addressed in a couple ways that I can think of.

    1) update the XEP to support naming sessions and pass that information with packets originating from and destined to legacy networks within an element. For example:

     Client registers with Transport using 'session1' as the ID
     
     <iq to="aim.soashable.com" id="session1auth">
       <t:session xmlns:t="http://xmpp.oth/extensions#xep-0100" id="session1"/>
       
       <query xmlns="jabber:iq:register">
         <username>some aim user</username>
         <password>password</password>
       </query>
     </iq>
    
     Server acknowledges, registration success
    
     <iq from="aim.soashable.com">
       <t:session xmlns:t="http://xmpp.oth/extensions#xep-0100" id="session1"/>
       <query xmlns="jabber:iq:register"/>
     </iq>
    
    
     Client sends a message to a user on the legacy network of "session1"
    
     <message to="someuser@aim.soashable.com">
       <t:session xmlns:t="http://xmpp.oth/extensions#xep-0100" id="session1"/>
       <body>Otherwise normal outgoing message</body>
     </message>
    
     Client receives a message from a user on the legacy network of "session1"
    
     <message from="someuser@aim.soashable.com">
       <t:session xmlns:t="http://xmpp.oth/extensions#xep-0100" id="session1"/>
       <body>Otherwise normal incoming message</body>
     </message>
    
    

    2) Implement support for multiple streams within a single BOSH connection, and link accounts on the client side (ie. don't even acknowledge it in the GUI).

     XMPP (username or anonymous @soashable.com)
     
     XMPP (anonymous@soashable.com)
      |
      +- AIM Transport 1
    
     XMPP (anonymous@soashable.com)
      |
      +- Yahoo! Transport 1
    
     XMPP (anonymous@soashable.com)
      |
      +- MSN Transport 1
    
     XMPP (anonymous@soashable.com)
      |
      +- AIM Transport 2
    
     Other XMPP (GTalk, etc)
    

    This option is far less than ideal in terms of resource usage and also completely neglecting the single connection aspect that XEP-100 should address.

Everything as an EXTJS Component

All class inheritance happens using Ext.extend. The standard pattern is deviated from a bit to work with JSDoc as it exists today, and that is rather than defining override methods anonymously in the call to Ext.extend, they are defined on the class prototype (as would happen without Ext), and then the prototype is passed into Ext.extend.

Classes (visual component or not) typically follow this pattern:

 Ext.namespace( "Soashable.Dialog" );

 Soashable.Dialog.MyDialog = function(config) {
   Soashable.Dialog.MyDialog.superclass.constructor.call( this, config );
 }

 Soashable.Dialog.MyDialog.prototype = {
   doSomething: function() {

   }
 }

 Ext.extend( Soashable.Dialog.MyDialog, Ext.Window, Soashable.Dialog.MyDialog.prototype );

Dialogs are Stupid. Event Handlers are Smart.

In order to encourage testability, it is important that dialgs are as stupid as possible. This means that they shouldn't know anything about Xmpp4Js--they should not send their own packets or add their own packet listeners. Instead, they should raise events and pass applicable arguments.

For example, an IM window should not do this:

 onSendClicked: function() {
  var msg = new Xmpp4Js.Packet.Message( this.participant, this.textBox.getValue() );
  this.con.send( msg );
 }

But rather this:

 onSendClicked: function() {
  // creator of this dialog knows what to do...
  this.fireEvent( "sendclicked", this.participant, this.textBox.getValue() );
 }

In practice this is not followed very strictly, and may change if it becomes unreasonable. It'd love to hear arguments either way. In particular, it does convolute the design and the actual call to send the packet has to be tested somewhere.

The main advantate such a high level of separation might provide is making the logic reusable across different UIs (iPhone, Web, Wii, etc) with their own dialog classes. That may be a moot point though, because those platforms would likely have such different UI needs that it may not be worth while to even try to share code.

I18n

Soashable components currently support i18n, but this is only one language. The pattern is basically this:

 Soashable.Lang.EN = {
   someString = "some string";
 }

 var soashable = new Soashable( {lang: Soashable.Lang.EN} );
 var lang = soashable.getLang(); // returns the language map it was created it

And components should handle language like this:

 Soashable.Dialog.MyDialog = function(config) {
  this.lang = config.lang;
 }

All UI components follow the same model; each instance that is created from within another should be passed a "lang" config option, and use strings provided by it.

This is very elementary and more sophistocated needs will arise as the architecture evolves. Following the above pattern is important though, as it will make evolving much easier.

About Soashable Web Application

Soashable is a completely Open Source (GPL) instant messaging client, built on the Xmpp4Js (LGPL) XMPP library for Javascript. It communicates with an XMPP server using XEP-0124/XEP-0206 HTTP Binding, and provides end-user functionality comparable to Meebo (but is in no way affiliated with Meebo).

About Soashable

Developer Docs

Project Documentation

Powered By