[kune-commits] r1634 - in trunk/src: main/java/cc/kune/chat/client main/java/cc/kune/common/client/notify main/java/cc/kune/core/client/auth main/java/cc/kune/core/client/notify/msgs main/java/cc/kune/msgs main/java/cc/kune/msgs/client test/java/cc/kune test/java/cc/kune/msgs test/java/cc/kune/msgs/client

Vicente J. Ruiz Jurado vjrj_ at ourproject.org
Tue Dec 6 02:42:08 CET 2011


Author: vjrj_
Date: 2011-12-06 02:42:07 +0100 (Tue, 06 Dec 2011)
New Revision: 1634

Added:
   trunk/src/main/java/cc/kune/msgs/client/UserMessageWidget.java
   trunk/src/main/java/cc/kune/msgs/client/UserMessageWidget.ui.xml
   trunk/src/test/java/cc/kune/msgs/
   trunk/src/test/java/cc/kune/msgs/client/
   trunk/src/test/java/cc/kune/msgs/client/UserMessagesPresenterTest.java
Removed:
   trunk/src/main/java/cc/kune/msgs/client/UserMessage.ui.xml
Modified:
   trunk/src/main/java/cc/kune/chat/client/ChatClientDefault.java
   trunk/src/main/java/cc/kune/chat/client/KuneChatNotifier.java
   trunk/src/main/java/cc/kune/chat/client/KuneHablarSignals.java
   trunk/src/main/java/cc/kune/common/client/notify/NotifyLevel.java
   trunk/src/main/java/cc/kune/common/client/notify/NotifyUser.java
   trunk/src/main/java/cc/kune/core/client/auth/AnonUsersManager.java
   trunk/src/main/java/cc/kune/core/client/notify/msgs/UserNotifierViewImpl.java
   trunk/src/main/java/cc/kune/core/client/notify/msgs/UserNotifyEvent.java
   trunk/src/main/java/cc/kune/msgs/KuneMsgs.gwt.xml
   trunk/src/main/java/cc/kune/msgs/client/UserMessage.java
   trunk/src/main/java/cc/kune/msgs/client/UserMessagesPanel.java
   trunk/src/main/java/cc/kune/msgs/client/UserMessagesPresenter.java
Log:
CLOSED - # 98: When using NotifyUser.info/etc messages of the same level must be show together 
http://kune.ourproject.org/issues/ticket/98
CLOSED - # 97: Allow NotifyUser with images (useful for chat notifications) 
http://kune.ourproject.org/issues/ticket/97
CLOSED - # 168: "You did not sign in" box remains after I've registered 
http://kune.ourproject.org/issues/ticket/168

Modified: trunk/src/main/java/cc/kune/chat/client/ChatClientDefault.java
===================================================================
--- trunk/src/main/java/cc/kune/chat/client/ChatClientDefault.java	2011-12-05 20:42:43 UTC (rev 1633)
+++ trunk/src/main/java/cc/kune/chat/client/ChatClientDefault.java	2011-12-06 01:42:07 UTC (rev 1634)
@@ -44,6 +44,7 @@
 import cc.kune.core.client.init.AppStartEvent;
 import cc.kune.core.client.init.AppStopEvent;
 import cc.kune.core.client.resources.CoreResources;
+import cc.kune.core.client.services.FileDownloadUtils;
 import cc.kune.core.client.sitebar.SitebarActions;
 import cc.kune.core.client.state.Session;
 import cc.kune.core.client.state.UserSignInEvent;
@@ -132,6 +133,7 @@
   private final ChatOptions chatOptions;
   private final ChatResources chatResources;
   private Dialog dialog;
+  private final FileDownloadUtils downUtils;
   private final I18nTranslationService i18n;
   private final EventBus kuneEventBus;
   private final CoreResources res;
@@ -141,16 +143,18 @@
   private final GlobalShortcutRegister shorcutRegister;
   private final SitebarActions siteActions;
   private final SubscriptionManager subscriptionManager;
+
   private final XmppSession xmppSession;
 
   @Inject
   public ChatClientDefault(final EventBus kuneEventBus, final I18nTranslationService i18n,
       final SitebarActions siteActions, final Session session, final CoreResources res,
-      final GlobalShortcutRegister shorcutRegister, final ChatOptions chatOptions,
-      final ChatResources chatResources, final ChatInstances chatInstances) {
+      final FileDownloadUtils downUtils, final GlobalShortcutRegister shorcutRegister,
+      final ChatOptions chatOptions, final ChatResources chatResources, final ChatInstances chatInstances) {
     this.kuneEventBus = kuneEventBus;
     this.i18n = i18n;
     this.res = res;
+    this.downUtils = downUtils;
     this.chatInstances = chatInstances;
     action = new ChatClientAction(chatResources);
     this.siteActions = siteActions;
@@ -322,7 +326,7 @@
     final KuneHablarWidget widget = new KuneHablarWidget(config.layout, config.tabHeaderSize);
     final Hablar hablar = widget.getHablar();
     HablarComplete.install(hablar, config);
-    new KuneHablarSignals(kuneEventBus, xmppSession, hablar, action, chatInstances);
+    new KuneHablarSignals(kuneEventBus, xmppSession, hablar, action, chatInstances, i18n, downUtils);
     if (htmlConfig.hasLogger) {
       new HablarConsole(hablar);
     }

Modified: trunk/src/main/java/cc/kune/chat/client/KuneChatNotifier.java
===================================================================
--- trunk/src/main/java/cc/kune/chat/client/KuneChatNotifier.java	2011-12-05 20:42:43 UTC (rev 1633)
+++ trunk/src/main/java/cc/kune/chat/client/KuneChatNotifier.java	2011-12-06 01:42:07 UTC (rev 1634)
@@ -20,24 +20,45 @@
 package cc.kune.chat.client;
 
 import cc.kune.common.client.notify.NotifyUser;
+import cc.kune.core.client.services.FileDownloadUtils;
+import cc.kune.core.shared.i18n.I18nTranslationService;
 
 import com.calclab.hablar.signals.client.notifications.HablarNotifier;
+import com.google.gwt.regexp.shared.MatchResult;
+import com.google.gwt.regexp.shared.RegExp;
 
 public class KuneChatNotifier implements HablarNotifier {
+  private final FileDownloadUtils downUtils;
+  private final I18nTranslationService i18n;
+  private final RegExp regExp;
 
-    @Override
-    public String getDisplayName() {
-        return "Bottom notifier";
-    }
+  public KuneChatNotifier(final I18nTranslationService i18n, final FileDownloadUtils downUtils) {
+    this.i18n = i18n;
+    this.downUtils = downUtils;
+    regExp = RegExp.compile("User (.*) says «(.*)»");
+  }
 
-    @Override
-    public String getId() {
-        return "kuneChatNotifier";
-    }
+  @Override
+  public String getDisplayName() {
+    return "Bottom notifier";
+  }
 
-    @Override
-    public void show(final String userMessage, final String messageType) {
-        NotifyUser.info(userMessage);
+  @Override
+  public String getId() {
+    return "kuneChatNotifier";
+  }
+
+  @Override
+  public void show(final String userMessage, final String messageType) {
+    // FIXME Dirty hack while emite/hablar lib don't provide user info
+    if (regExp.test(userMessage)) {
+      final MatchResult m = regExp.exec(userMessage);
+      final String user = m.getGroup(1);
+      NotifyUser.avatar(downUtils.getUserAvatar(user),
+          i18n.t("User [%s] says «[%s]»", user, m.getGroup(2)));
+    } else {
+      NotifyUser.info(userMessage);
     }
+  }
 
 }

Modified: trunk/src/main/java/cc/kune/chat/client/KuneHablarSignals.java
===================================================================
--- trunk/src/main/java/cc/kune/chat/client/KuneHablarSignals.java	2011-12-05 20:42:43 UTC (rev 1633)
+++ trunk/src/main/java/cc/kune/chat/client/KuneHablarSignals.java	2011-12-06 01:42:07 UTC (rev 1634)
@@ -20,6 +20,8 @@
 package cc.kune.chat.client;
 
 import cc.kune.chat.client.ChatClientDefault.ChatClientAction;
+import cc.kune.core.client.services.FileDownloadUtils;
+import cc.kune.core.shared.i18n.I18nTranslationService;
 
 import com.calclab.emite.core.client.xmpp.session.XmppSession;
 import com.calclab.emite.xep.storage.client.PrivateStorageManager;
@@ -34,7 +36,6 @@
 import com.calclab.hablar.signals.client.unattended.UnattendedPagesManager;
 import com.calclab.hablar.signals.client.unattended.UnattendedPresenter;
 import com.calclab.hablar.user.client.UserContainer;
-import com.google.gwt.core.client.GWT;
 import com.google.gwt.event.shared.EventBus;
 import com.google.gwt.user.client.Window;
 import com.google.gwt.user.client.ui.HasText;
@@ -70,7 +71,8 @@
   // FIXME: move to gin
   @SuppressWarnings("deprecation")
   public KuneHablarSignals(final EventBus kuneEventBus, final XmppSession session, final Hablar hablar,
-      final ChatClientAction action, final ChatInstances chatInstances) {
+      final ChatClientAction action, final ChatInstances chatInstances,
+      final I18nTranslationService i18n, final FileDownloadUtils downUtils) {
     final HablarEventBus hablarEventBus = hablar.getEventBus();
     final PrivateStorageManager storageManager = chatInstances.privateStorageManager;
 
@@ -97,11 +99,10 @@
     // notificationManager.addNotifier((BrowserPopupHablarNotifier)
     // GWT.create(BrowserPopupHablarNotifier.class),
     // true);
-    notificationManager.addNotifier((KuneChatNotifier) GWT.create(KuneChatNotifier.class), true);
+    notificationManager.addNotifier(new KuneChatNotifier(i18n, downUtils), true);
 
     final SignalsPreferencesPresenter preferencesPage = new SignalsPreferencesPresenter(session,
         storageManager, hablarEventBus, preferences, new SignalsPreferencesWidget(), notificationManager);
     hablar.addPage(preferencesPage, UserContainer.ROL);
   }
-
 }

Modified: trunk/src/main/java/cc/kune/common/client/notify/NotifyLevel.java
===================================================================
--- trunk/src/main/java/cc/kune/common/client/notify/NotifyLevel.java	2011-12-05 20:42:43 UTC (rev 1633)
+++ trunk/src/main/java/cc/kune/common/client/notify/NotifyLevel.java	2011-12-06 01:42:07 UTC (rev 1634)
@@ -20,5 +20,24 @@
 package cc.kune.common.client.notify;
 
 public enum NotifyLevel {
-    error, important, info, log, veryImportant
+  avatar, error, important, info, log, veryImportant;
+
+  private String url;
+
+  NotifyLevel() {
+    this(null);
+  }
+
+  NotifyLevel(final String url) {
+    this.url = url;
+  }
+
+  public String getUrl() {
+    return url;
+  }
+
+  public NotifyLevel url(final String url) {
+    this.url = url;
+    return this;
+  }
 }

Modified: trunk/src/main/java/cc/kune/common/client/notify/NotifyUser.java
===================================================================
--- trunk/src/main/java/cc/kune/common/client/notify/NotifyUser.java	2011-12-05 20:42:43 UTC (rev 1633)
+++ trunk/src/main/java/cc/kune/common/client/notify/NotifyUser.java	2011-12-06 01:42:07 UTC (rev 1634)
@@ -44,6 +44,10 @@
     askConfirmation(null, title, message, callback);
   }
 
+  public static void avatar(final String url, final String message) {
+    eventBus.fireEvent(new UserNotifyEvent(NotifyLevel.avatar.url(url), message));
+  }
+
   public static void error(final String message) {
     eventBus.fireEvent(new UserNotifyEvent(NotifyLevel.error, message));
   }

Modified: trunk/src/main/java/cc/kune/core/client/auth/AnonUsersManager.java
===================================================================
--- trunk/src/main/java/cc/kune/core/client/auth/AnonUsersManager.java	2011-12-05 20:42:43 UTC (rev 1633)
+++ trunk/src/main/java/cc/kune/core/client/auth/AnonUsersManager.java	2011-12-06 01:42:07 UTC (rev 1634)
@@ -19,26 +19,29 @@
  */
 package cc.kune.core.client.auth;
 
-import cc.kune.common.client.notify.NotifyUser;
+import cc.kune.common.client.notify.NotifyLevel;
 import cc.kune.common.client.utils.TextUtils;
 import cc.kune.core.client.cookies.CookiesManager;
 import cc.kune.core.client.i18n.I18nUITranslationService;
 import cc.kune.core.client.init.AppStartEvent;
 import cc.kune.core.client.init.AppStartEvent.AppStartHandler;
+import cc.kune.core.client.notify.msgs.UserNotifyEvent;
 import cc.kune.core.client.state.Session;
 import cc.kune.core.client.state.SiteTokens;
 import cc.kune.core.client.state.UserSignInEvent;
 import cc.kune.core.client.state.UserSignInEvent.UserSignInHandler;
 
+import com.google.gwt.event.shared.EventBus;
 import com.google.inject.Inject;
 
 public class AnonUsersManager {
 
   public static final String ANON_MESSAGE_CLOSE_ICON = "k-anon-um-close-btn";
+  protected UserNotifyEvent notifyMsg;
 
   @Inject
   public AnonUsersManager(final Session session, final CookiesManager cookiesManager,
-      final I18nUITranslationService i18n) {
+      final I18nUITranslationService i18n, final EventBus eventBus) {
     session.onAppStart(true, new AppStartHandler() {
       @Override
       public void onAppStart(final AppStartEvent event) {
@@ -52,13 +55,16 @@
             final String signin = TextUtils.generateHtmlLink("#" + SiteTokens.SIGNIN,
                 i18n.tWithNT("sign in", "register, in lowercase"), false);
             final String siteCommonName = i18n.getSiteCommonName();
-            NotifyUser.info(
+            notifyMsg = UserNotifyEvent.fire(
+                eventBus,
+                NotifyLevel.info,
                 "",
                 i18n.tWithNT(
                     "You did not sign-in, so you can just see some public contents in [%s], "
                         + "but not edit or collaborate with others. Please [%s] or [%s] in order to get full access to [%s] tools and contents",
                     "This will be something like 'Please register or sign in in other to get full access to this site tools', but instead of %s some links",
-                    siteCommonName, register, signin, siteCommonName), ANON_MESSAGE_CLOSE_ICON, true);
+                    siteCommonName, register, signin, siteCommonName), Boolean.TRUE);
+            notifyMsg.setId(ANON_MESSAGE_CLOSE_ICON);
           } else {
             if (Boolean.valueOf(anonCookie)) {
               // Registered already: we set the cookie for some big period again
@@ -74,6 +80,10 @@
       @Override
       public void onUserSignIn(final UserSignInEvent event) {
         cookiesManager.setAnonCookie(true);
+        if (notifyMsg != null) {
+          // Isue #168, after register/sign-in the message not remains visible
+          notifyMsg.getCloser().close();
+        }
       }
     });
   }

Modified: trunk/src/main/java/cc/kune/core/client/notify/msgs/UserNotifierViewImpl.java
===================================================================
--- trunk/src/main/java/cc/kune/core/client/notify/msgs/UserNotifierViewImpl.java	2011-12-05 20:42:43 UTC (rev 1633)
+++ trunk/src/main/java/cc/kune/core/client/notify/msgs/UserNotifierViewImpl.java	2011-12-06 01:42:07 UTC (rev 1634)
@@ -22,6 +22,7 @@
 import cc.kune.common.client.ui.PopupBottomPanel;
 import cc.kune.core.client.notify.msgs.UserNotifierPresenter.UserNotifierView;
 import cc.kune.msgs.client.CloseCallback;
+import cc.kune.msgs.client.UserMessage;
 import cc.kune.msgs.client.UserMessagesPanel;
 import cc.kune.msgs.client.UserMessagesPresenter;
 
@@ -32,36 +33,45 @@
 import com.gwtplatform.mvp.client.PopupViewImpl;
 
 public class UserNotifierViewImpl extends PopupViewImpl implements UserNotifierView {
-    private final UserMessagesPresenter msgs;
-    private final PopupBottomPanel popup;
+  private final UserMessagesPresenter msgs;
+  private final PopupBottomPanel popup;
 
-    @Inject
-    public UserNotifierViewImpl(final EventBus eventBus, final UserMessagesPresenter msgs, final UserMessagesPanel panel) {
-        super(eventBus);
-        this.msgs = msgs;
-        msgs.init(panel);
-        panel.setWidth("370px");
-        popup = new PopupBottomPanel(false, false);
-        popup.add(panel);
-        popup.show();
-    }
+  @Inject
+  public UserNotifierViewImpl(final EventBus eventBus, final UserMessagesPresenter msgs,
+      final UserMessagesPanel panel) {
+    super(eventBus);
+    this.msgs = msgs;
+    msgs.init(panel);
+    panel.setWidth("370px");
+    popup = new PopupBottomPanel(false, false);
+    popup.add(panel);
+    popup.show();
+  }
 
-    @Override
-    public Widget asWidget() {
-        return popup;
-    }
+  @Override
+  public Widget asWidget() {
+    return popup;
+  }
 
-    @Override
-    public void notify(final UserNotifyEvent event) {
-        msgs.add(event.getLevel(), event.getTitle(), event.getMessage(), event.getId(), event.getCloseable(),
-                new CloseCallback() {
-                    @Override
-                    public void onClose() {
-                        popup.setCenterPosition();
-                    }
-                });
-        popup.setCenterPosition();
-        DOM.setStyleAttribute(popup.getElement(), "zIndex", "100000");
-    }
+  @Override
+  public void notify(final UserNotifyEvent event) {
+    final UserMessage msg = msgs.add(event.getLevel(), event.getTitle(), event.getMessage(),
+        event.getId(), event.getCloseable(), new CloseCallback() {
+          @Override
+          public void onClose() {
+            popup.setCenterPosition();
+          }
+        });
+    event.setCloser(new UserNotifyEvent.UserNotifyCloser() {
+      @Override
+      public void close() {
+        if (msg.isAttached()) {
+          msg.close();
+        }
+      }
+    });
+    popup.setCenterPosition();
+    DOM.setStyleAttribute(popup.getElement(), "zIndex", "100000");
+  }
 
 }

Modified: trunk/src/main/java/cc/kune/core/client/notify/msgs/UserNotifyEvent.java
===================================================================
--- trunk/src/main/java/cc/kune/core/client/notify/msgs/UserNotifyEvent.java	2011-12-05 20:42:43 UTC (rev 1633)
+++ trunk/src/main/java/cc/kune/core/client/notify/msgs/UserNotifyEvent.java	2011-12-06 01:42:07 UTC (rev 1634)
@@ -20,6 +20,7 @@
 package cc.kune.core.client.notify.msgs;
 
 import cc.kune.common.client.notify.NotifyLevel;
+import cc.kune.msgs.client.CloseCallback;
 
 import com.google.gwt.event.shared.EventHandler;
 import com.google.gwt.event.shared.GwtEvent;
@@ -28,101 +29,127 @@
 
 public class UserNotifyEvent extends GwtEvent<UserNotifyEvent.UserNotifyHandler> {
 
-    public interface HasUserNotifyHandlers extends HasHandlers {
-        HandlerRegistration addUserNotifyHandler(UserNotifyHandler handler);
-    }
+  public interface HasUserNotifyHandlers extends HasHandlers {
+    HandlerRegistration addUserNotifyHandler(UserNotifyHandler handler);
+  }
 
-    public interface UserNotifyHandler extends EventHandler {
-        public void onUserNotify(UserNotifyEvent event);
-    }
+  public interface UserNotifyCloser {
+    public void close();
+  }
 
-    private static final Type<UserNotifyHandler> TYPE = new Type<UserNotifyHandler>();
+  public interface UserNotifyHandler extends EventHandler {
+    public void onUserNotify(UserNotifyEvent event);
+  }
 
-    public static void fire(final HasHandlers source, final NotifyLevel level, final java.lang.String message) {
-        source.fireEvent(new UserNotifyEvent(level, "", message));
-    }
+  private static final Type<UserNotifyHandler> TYPE = new Type<UserNotifyHandler>();
 
-    public static void fire(final HasHandlers source, final NotifyLevel level, final java.lang.String message,
-            final Boolean closeable) {
-        source.fireEvent(new UserNotifyEvent(level, "", message, closeable));
-    }
+  public static void fire(final HasHandlers source, final NotifyLevel level,
+      final java.lang.String message) {
+    source.fireEvent(new UserNotifyEvent(level, "", message));
+  }
 
-    public static void fire(final HasHandlers source, final NotifyLevel level, final java.lang.String title,
-            final java.lang.String message) {
-        source.fireEvent(new UserNotifyEvent(level, title, message));
-    }
+  public static void fire(final HasHandlers source, final NotifyLevel level,
+      final java.lang.String message, final Boolean closeable) {
+    source.fireEvent(new UserNotifyEvent(level, "", message, closeable));
+  }
 
-    public static void fire(final HasHandlers source, final NotifyLevel level, final java.lang.String title,
-            final java.lang.String message, final Boolean closeable) {
-        source.fireEvent(new UserNotifyEvent(level, title, message, closeable));
-    }
+  public static void fire(final HasHandlers source, final NotifyLevel level,
+      final java.lang.String title, final java.lang.String message) {
+    source.fireEvent(new UserNotifyEvent(level, title, message));
+  }
 
-    public static Type<UserNotifyHandler> getType() {
-        return TYPE;
-    }
+  public static UserNotifyEvent fire(final HasHandlers source, final NotifyLevel level,
+      final java.lang.String title, final java.lang.String message, final Boolean closeable) {
+    final UserNotifyEvent event = new UserNotifyEvent(level, title, message, closeable);
+    source.fireEvent(event);
+    return event;
+  }
 
-    private final Boolean closeable;
-    private java.lang.String id;
-    private final NotifyLevel level;
-    private final java.lang.String message;
-    private final java.lang.String title;
+  public static Type<UserNotifyHandler> getType() {
+    return TYPE;
+  }
 
-    public UserNotifyEvent(final NotifyLevel level, final java.lang.String message) {
-        this(level, "", message, false);
-    }
+  private final Boolean closeable;
+  private CloseCallback closeCallback;
+  private UserNotifyCloser closer;
+  private java.lang.String id;
+  private final NotifyLevel level;
+  private final java.lang.String message;
+  private final java.lang.String title;
 
-    public UserNotifyEvent(final NotifyLevel level, final java.lang.String message, final Boolean closeable) {
-        this(level, "", message, closeable);
-    }
+  public UserNotifyEvent(final NotifyLevel level, final java.lang.String message) {
+    this(level, "", message, false);
+  }
 
-    public UserNotifyEvent(final NotifyLevel level, final java.lang.String title, final java.lang.String message) {
-        this(level, title, message, false);
-    }
+  public UserNotifyEvent(final NotifyLevel level, final java.lang.String message, final Boolean closeable) {
+    this(level, "", message, closeable);
+  }
 
-    public UserNotifyEvent(final NotifyLevel level, final java.lang.String title, final java.lang.String message,
-            final Boolean closeable) {
-        this.level = level;
-        this.title = title;
-        this.message = message;
-        this.closeable = closeable;
-    }
+  public UserNotifyEvent(final NotifyLevel level, final java.lang.String title,
+      final java.lang.String message) {
+    this(level, title, message, false);
+  }
 
-    public UserNotifyEvent(final String message) {
-        this(NotifyLevel.info, message);
-    }
+  public UserNotifyEvent(final NotifyLevel level, final java.lang.String title,
+      final java.lang.String message, final Boolean closeable) {
+    this.level = level;
+    this.title = title;
+    this.message = message;
+    this.closeable = closeable;
+  }
 
-    @Override
-    protected void dispatch(final UserNotifyHandler handler) {
-        handler.onUserNotify(this);
-    }
+  public UserNotifyEvent(final String message) {
+    this(NotifyLevel.info, message);
+  }
 
-    @Override
-    public Type<UserNotifyHandler> getAssociatedType() {
-        return TYPE;
-    }
+  @Override
+  protected void dispatch(final UserNotifyHandler handler) {
+    handler.onUserNotify(this);
+  }
 
-    public Boolean getCloseable() {
-        return closeable;
-    }
+  @Override
+  public Type<UserNotifyHandler> getAssociatedType() {
+    return TYPE;
+  }
 
-    public java.lang.String getId() {
-        return id;
-    }
+  public Boolean getCloseable() {
+    return closeable;
+  }
 
-    public NotifyLevel getLevel() {
-        return level;
-    }
+  public CloseCallback getCloseCallback() {
+    return closeCallback;
+  }
 
-    public java.lang.String getMessage() {
-        return message;
-    }
+  public UserNotifyCloser getCloser() {
+    return closer;
+  }
 
-    public java.lang.String getTitle() {
-        return title;
-    }
+  public java.lang.String getId() {
+    return id;
+  }
 
-    public void setId(final String id) {
-        this.id = id;
-    }
+  public NotifyLevel getLevel() {
+    return level;
+  }
 
+  public java.lang.String getMessage() {
+    return message;
+  }
+
+  public java.lang.String getTitle() {
+    return title;
+  }
+
+  public void setCloseCallback(final CloseCallback closeCallback) {
+    this.closeCallback = closeCallback;
+  }
+
+  public void setCloser(final UserNotifyCloser closer) {
+    this.closer = closer;
+  }
+
+  public void setId(final String id) {
+    this.id = id;
+  }
+
 }

Modified: trunk/src/main/java/cc/kune/msgs/KuneMsgs.gwt.xml
===================================================================
--- trunk/src/main/java/cc/kune/msgs/KuneMsgs.gwt.xml	2011-12-05 20:42:43 UTC (rev 1633)
+++ trunk/src/main/java/cc/kune/msgs/KuneMsgs.gwt.xml	2011-12-06 01:42:07 UTC (rev 1634)
@@ -1,14 +1,15 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <module>
   <!-- Inherit the core Web Toolkit stuff. -->
-  <inherits name='com.google.gwt.user.User' />
-  <inherits name="org.adamtacy.GWTEffects" />
-  <inherits name="cc.kune.common.KuneCommon" />
+  <inherits name='com.google.gwt.user.User'/>
+  <inherits name="org.adamtacy.GWTEffects"/>
+  <inherits name="cc.kune.common.KuneCommon"/>
   <!-- Other module inherits -->
   <!-- Specify the app entry point class. -->
   <!-- <entry-point class='cc.kune.msgs.client.KuneMessagesSampleEntryPoint'
     /> -->
   <!-- Specify the paths for translatable code -->
-  <source path='client' />
-  <public path="public" />
+  <source path='client'
+    excludes="**/*Test.java,**/*Tests.java,**/*MockProvider.java"/>
+  <public path="public"/>
 </module>
\ No newline at end of file

Modified: trunk/src/main/java/cc/kune/msgs/client/UserMessage.java
===================================================================
--- trunk/src/main/java/cc/kune/msgs/client/UserMessage.java	2011-12-05 20:42:43 UTC (rev 1633)
+++ trunk/src/main/java/cc/kune/msgs/client/UserMessage.java	2011-12-06 01:42:07 UTC (rev 1634)
@@ -1,168 +1,10 @@
-/*
- *
- * Copyright (C) 2007-2011 The kune development team (see CREDITS for details)
- * This file is part of kune.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- */
 package cc.kune.msgs.client;
 
-import org.adamtacy.client.ui.effects.events.EffectCompletedEvent;
-import org.adamtacy.client.ui.effects.events.EffectCompletedHandler;
-import org.adamtacy.client.ui.effects.examples.Fade;
-import org.adamtacy.client.ui.effects.examples.Show;
+public interface UserMessage {
 
-import cc.kune.common.client.notify.NotifyLevel;
-import cc.kune.common.client.utils.TextUtils;
-import cc.kune.msgs.client.resources.UserMessageImagesUtil;
+  void appendMsg(String message);
 
-import com.google.gwt.core.client.GWT;
-import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.safehtml.client.SafeHtmlTemplates;
-import com.google.gwt.safehtml.shared.SafeHtml;
-import com.google.gwt.safehtml.shared.SafeHtmlUtils;
-import com.google.gwt.safehtml.shared.SimpleHtmlSanitizer;
-import com.google.gwt.uibinder.client.UiBinder;
-import com.google.gwt.uibinder.client.UiField;
-import com.google.gwt.uibinder.client.UiHandler;
-import com.google.gwt.user.client.Timer;
-import com.google.gwt.user.client.ui.Composite;
-import com.google.gwt.user.client.ui.HasText;
-import com.google.gwt.user.client.ui.Image;
-import com.google.gwt.user.client.ui.InlineHTML;
-import com.google.gwt.user.client.ui.PushButton;
-import com.google.gwt.user.client.ui.Widget;
+  void close();
 
-public class UserMessage extends Composite implements HasText {
-
-  interface MessageUiBinder extends UiBinder<Widget, UserMessage> {
-  }
-
-  public interface MsgTemplate extends SafeHtmlTemplates {
-    @Template("<span>{0}</span>")
-    SafeHtml format(SafeHtml message);
-  }
-
-  public interface MsgWithTitleTemplate extends SafeHtmlTemplates {
-    @Template("<span><span class=\"k-msg-title\">{0}</span><p>{1}</p></span>")
-    SafeHtml format(SafeHtml title, SafeHtml message);
-  }
-
-  private static String closeTitle = "Close";
-
-  private static int fadeMills = 5000;
-
-  private static final MsgTemplate MSG_NO_TITLE = GWT.create(MsgTemplate.class);
-  private static final MsgWithTitleTemplate MSG_WITH_TITLE = GWT.create(MsgWithTitleTemplate.class);
-
-  private static MessageUiBinder uiBinder = GWT.create(MessageUiBinder.class);
-
-  public static void setCloseTitle(final String title) {
-    closeTitle = title;
-  }
-
-  public static void setFadeMills(final int mills) {
-    fadeMills = mills;
-  }
-
-  @UiField
-  PushButton close;
-
-  private final CloseCallback closeCallback;
-
-  @UiField
-  Image icon;
-
-  @UiField
-  InlineHTML label;
-
-  public UserMessage(final NotifyLevel level, final String title, final String message, final String id,
-      final boolean closeable, final CloseCallback closeCallback) {
-    this.closeCallback = closeCallback;
-    initWidget(uiBinder.createAndBindUi(this));
-    if (TextUtils.notEmpty(id)) {
-      close.ensureDebugId(id);
-    }
-    if (TextUtils.notEmpty(message)) {
-      if (TextUtils.notEmpty(title)) {
-        label.setHTML(MSG_WITH_TITLE.format(SimpleHtmlSanitizer.sanitizeHtml(title), sanitize(message)));
-      } else {
-        label.setHTML(MSG_NO_TITLE.format(sanitize(message)));
-      }
-      close.setVisible(closeable);
-      close.setTitle(closeTitle);
-      if (!closeable) {
-        final Timer time = new Timer() {
-          @Override
-          public void run() {
-            close();
-          }
-        };
-        time.schedule(fadeMills);
-      }
-      icon.setResource(UserMessageImagesUtil.getIcon(level));
-      final Show anim = new Show(this.getElement());
-      anim.setDuration(0.5);
-      anim.play();
-    }
-  }
-
-  public UserMessage(final String message, final CloseCallback closeCallback) {
-    this("", message, false, closeCallback);
-  }
-
-  public UserMessage(final String title, final String message, final boolean closeable,
-      final CloseCallback closeCallback) {
-    this(NotifyLevel.info, title, message, "", closeable, closeCallback);
-  }
-
-  public UserMessage(final String title, final String message, final CloseCallback closeCallback) {
-    this(title, message, false, closeCallback);
-  }
-
-  private void close() {
-    final Fade fade = new Fade(this.getElement());
-    fade.setDuration(0.7);
-    fade.play();
-    fade.addEffectCompletedHandler(new EffectCompletedHandler() {
-
-      @Override
-      public void onEffectCompleted(final EffectCompletedEvent event) {
-        removeFromParent();
-        closeCallback.onClose();
-      }
-    });
-  }
-
-  @Override
-  public String getText() {
-    return label.getText();
-  }
-
-  @UiHandler("close")
-  void handleClick(final ClickEvent e) {
-    close();
-  }
-
-  private SafeHtml sanitize(final String message) {
-    return SafeHtmlUtils.fromTrustedString(message);
-  }
-
-  @Override
-  public void setText(final String text) {
-    label.setText(text);
-  }
-
+  boolean isAttached();
 }

Deleted: trunk/src/main/java/cc/kune/msgs/client/UserMessage.ui.xml
===================================================================
--- trunk/src/main/java/cc/kune/msgs/client/UserMessage.ui.xml	2011-12-05 20:42:43 UTC (rev 1633)
+++ trunk/src/main/java/cc/kune/msgs/client/UserMessage.ui.xml	2011-12-06 01:42:07 UTC (rev 1634)
@@ -1,36 +0,0 @@
-<!DOCTYPE ui:UiBinder SYSTEM "http://dl.google.com/gwt/DTD/xhtml.ent">
-<ui:UiBinder xmlns:ui="urn:ui:com.google.gwt.uibinder"
-  xmlns:g="urn:import:com.google.gwt.user.client.ui">
-
-  <ui:style>
-    @if user.agent safari {
-      .k-msg-width {
-        width: 0px;
-      }
-    }
-
-    @else {
-      .k-msg-width {
-        width: auto;
-      }
-    }
-  </ui:style>
-  <ui:with field='img'
-    type='cc.kune.msgs.client.resources.UserMessageImages'/>
-  <g:HorizontalPanel verticalAlignment="ALIGN_MIDDLE"
-    height="42px" addStyleNames="k-msg, k-3corners, {style.k-msg-width}">
-    <g:SimplePanel addStyleNames="k-msg-icon">
-      <g:Image width="100%" ui:field="icon" height="100%"/>
-    </g:SimplePanel>
-    <g:Cell width="100%">
-      <g:InlineHTML ui:field="label" width="100%"
-        styleName="k-msg-label"/>
-    </g:Cell>
-    <g:PushButton ui:field="close" styleName="k-msg-close"
-      width="22px">
-      <g:upFace image='{img.remove}'/>
-      <g:downFace image='{img.removeOver}'/>
-      <g:upHoveringFace image='{img.removeGrey}'/>
-    </g:PushButton>
-  </g:HorizontalPanel>
-</ui:UiBinder>

Copied: trunk/src/main/java/cc/kune/msgs/client/UserMessageWidget.java (from rev 1630, trunk/src/main/java/cc/kune/msgs/client/UserMessage.java)
===================================================================
--- trunk/src/main/java/cc/kune/msgs/client/UserMessageWidget.java	                        (rev 0)
+++ trunk/src/main/java/cc/kune/msgs/client/UserMessageWidget.java	2011-12-06 01:42:07 UTC (rev 1634)
@@ -0,0 +1,198 @@
+/*
+ *
+ * Copyright (C) 2007-2011 The kune development team (see CREDITS for details)
+ * This file is part of kune.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+package cc.kune.msgs.client;
+
+import org.adamtacy.client.ui.effects.events.EffectCompletedEvent;
+import org.adamtacy.client.ui.effects.events.EffectCompletedHandler;
+import org.adamtacy.client.ui.effects.examples.Fade;
+import org.adamtacy.client.ui.effects.examples.Show;
+
+import cc.kune.common.client.notify.NotifyLevel;
+import cc.kune.common.client.utils.TextUtils;
+import cc.kune.msgs.client.resources.UserMessageImagesUtil;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.event.dom.client.ClickEvent;
+import com.google.gwt.safehtml.client.SafeHtmlTemplates;
+import com.google.gwt.safehtml.shared.SafeHtml;
+import com.google.gwt.safehtml.shared.SafeHtmlUtils;
+import com.google.gwt.safehtml.shared.SimpleHtmlSanitizer;
+import com.google.gwt.uibinder.client.UiBinder;
+import com.google.gwt.uibinder.client.UiField;
+import com.google.gwt.uibinder.client.UiHandler;
+import com.google.gwt.user.client.Timer;
+import com.google.gwt.user.client.ui.Composite;
+import com.google.gwt.user.client.ui.HasText;
+import com.google.gwt.user.client.ui.Image;
+import com.google.gwt.user.client.ui.InlineHTML;
+import com.google.gwt.user.client.ui.PushButton;
+import com.google.gwt.user.client.ui.Widget;
+
+public class UserMessageWidget extends Composite implements HasText, UserMessage {
+
+  public interface MsgTemplate extends SafeHtmlTemplates {
+    @Template("<span>{0}</span>")
+    SafeHtml format(SafeHtml message);
+  }
+
+  public interface MsgWithTitleTemplate extends SafeHtmlTemplates {
+    @Template("<span><span class=\"k-msg-title\">{0}</span><p>{1}</p></span>")
+    SafeHtml format(SafeHtml title, SafeHtml message);
+  }
+
+  interface UserMessageWidgetUiBinder extends UiBinder<Widget, UserMessageWidget> {
+  }
+
+  private static final String AVATAR_SIZE = "40px";
+
+  private static String closeTitle = "Close";
+
+  private static int fadeMills = 5000;
+
+  private static final MsgTemplate MSG_NO_TITLE = GWT.create(MsgTemplate.class);
+  private static final MsgWithTitleTemplate MSG_WITH_TITLE = GWT.create(MsgWithTitleTemplate.class);
+  private static UserMessageWidgetUiBinder uiBinder = GWT.create(UserMessageWidgetUiBinder.class);
+
+  public static void setCloseTitle(final String title) {
+    closeTitle = title;
+  }
+
+  public static void setFadeMills(final int mills) {
+    fadeMills = mills;
+  }
+
+  @UiField
+  PushButton close;
+
+  private final CloseCallback closeCallback;
+
+  @UiField
+  Image icon;
+
+  @UiField
+  InlineHTML label;
+
+  private Timer time;
+
+  public UserMessageWidget(final NotifyLevel level, final String title, final String message,
+      final String id, final boolean closeable, final CloseCallback closeCallback) {
+    this.closeCallback = closeCallback;
+    initWidget(uiBinder.createAndBindUi(this));
+    if (TextUtils.notEmpty(id)) {
+      close.ensureDebugId(id);
+    }
+    if (TextUtils.notEmpty(message)) {
+      setMsg(title, message);
+      close.setVisible(closeable);
+      close.setTitle(closeTitle);
+      if (!closeable) {
+        time = new Timer() {
+          @Override
+          public void run() {
+            close();
+          }
+        };
+        time.schedule(fadeMills);
+      }
+      setIcon(level);
+      final Show anim = new Show(this.getElement());
+      anim.setDuration(0.5);
+      anim.play();
+    }
+  }
+
+  public UserMessageWidget(final String message, final CloseCallback closeCallback) {
+    this("", message, false, closeCallback);
+  }
+
+  public UserMessageWidget(final String title, final String message, final boolean closeable,
+      final CloseCallback closeCallback) {
+    this(NotifyLevel.info, title, message, "", closeable, closeCallback);
+  }
+
+  public UserMessageWidget(final String title, final String message, final CloseCallback closeCallback) {
+    this(title, message, false, closeCallback);
+  }
+
+  @Override
+  public void appendMsg(final String message) {
+    resetTimer();
+    label.setHTML(sanitize(label.getHTML() + "<p>" + message + "</p>"));
+  }
+
+  @Override
+  public void close() {
+    final Fade fade = new Fade(this.getElement());
+    fade.setDuration(0.7);
+    fade.play();
+    fade.addEffectCompletedHandler(new EffectCompletedHandler() {
+
+      @Override
+      public void onEffectCompleted(final EffectCompletedEvent event) {
+        removeFromParent();
+        closeCallback.onClose();
+      }
+    });
+  }
+
+  @Override
+  public String getText() {
+    return label.getText();
+  }
+
+  @UiHandler("close")
+  void handleClick(final ClickEvent e) {
+    close();
+  }
+
+  private void resetTimer() {
+    if (time != null) {
+      // More time to read it!
+      time.schedule(fadeMills);
+    }
+  }
+
+  private SafeHtml sanitize(final String message) {
+    return SafeHtmlUtils.fromTrustedString(message);
+  }
+
+  private void setIcon(final NotifyLevel level) {
+    if (level.equals(NotifyLevel.avatar)) {
+      icon.setUrl(level.getUrl());
+      icon.setSize(AVATAR_SIZE, AVATAR_SIZE);
+    } else {
+      icon.setResource(UserMessageImagesUtil.getIcon(level));
+    }
+  }
+
+  private void setMsg(final String title, final String message) {
+    if (TextUtils.notEmpty(title)) {
+      label.setHTML(MSG_WITH_TITLE.format(SimpleHtmlSanitizer.sanitizeHtml(title), sanitize(message)));
+    } else {
+      label.setHTML(MSG_NO_TITLE.format(sanitize(message)));
+    }
+  }
+
+  @Override
+  public void setText(final String text) {
+    label.setText(text);
+  }
+
+}

Copied: trunk/src/main/java/cc/kune/msgs/client/UserMessageWidget.ui.xml (from rev 1630, trunk/src/main/java/cc/kune/msgs/client/UserMessage.ui.xml)
===================================================================
--- trunk/src/main/java/cc/kune/msgs/client/UserMessageWidget.ui.xml	                        (rev 0)
+++ trunk/src/main/java/cc/kune/msgs/client/UserMessageWidget.ui.xml	2011-12-06 01:42:07 UTC (rev 1634)
@@ -0,0 +1,36 @@
+<!DOCTYPE ui:UiBinder SYSTEM "http://dl.google.com/gwt/DTD/xhtml.ent">
+<ui:UiBinder xmlns:ui="urn:ui:com.google.gwt.uibinder"
+  xmlns:g="urn:import:com.google.gwt.user.client.ui">
+
+  <ui:style>
+    @if user.agent safari {
+      .k-msg-width {
+        width: 0px;
+      }
+    }
+
+    @else {
+      .k-msg-width {
+        width: auto;
+      }
+    }
+  </ui:style>
+  <ui:with field='img'
+    type='cc.kune.msgs.client.resources.UserMessageImages'/>
+  <g:HorizontalPanel verticalAlignment="ALIGN_MIDDLE"
+    addStyleNames="k-msg, k-3corners, {style.k-msg-width}">
+    <g:SimplePanel addStyleNames="k-msg-icon">
+      <g:Image width="100%" ui:field="icon" height="100%"/>
+    </g:SimplePanel>
+    <g:Cell width="100%">
+      <g:InlineHTML ui:field="label" width="100%"
+        styleName="k-msg-label"/>
+    </g:Cell>
+    <g:PushButton ui:field="close" styleName="k-msg-close"
+      width="22px">
+      <g:upFace image='{img.remove}'/>
+      <g:downFace image='{img.removeOver}'/>
+      <g:upHoveringFace image='{img.removeGrey}'/>
+    </g:PushButton>
+  </g:HorizontalPanel>
+</ui:UiBinder>

Modified: trunk/src/main/java/cc/kune/msgs/client/UserMessagesPanel.java
===================================================================
--- trunk/src/main/java/cc/kune/msgs/client/UserMessagesPanel.java	2011-12-05 20:42:43 UTC (rev 1633)
+++ trunk/src/main/java/cc/kune/msgs/client/UserMessagesPanel.java	2011-12-06 01:42:07 UTC (rev 1634)
@@ -40,8 +40,7 @@
 
   @UiField
   FlowPanel bottom;
-  private NotifyLevel currentLevel;
-  private UserMessage currentMsg;
+
   @UiField
   VerticalPanel panel;
 
@@ -50,12 +49,11 @@
   }
 
   @Override
-  public void add(final NotifyLevel level, final String title, final String message, final String id,
-      final boolean closeable, final CloseCallback closeCallback) {
+  public UserMessage add(final NotifyLevel level, final String title, final String message,
+      final String id, final Boolean closeable, final CloseCallback closeCallback) {
 
-    final UserMessage msg = new UserMessage(level, title, message, id, closeable, closeCallback);
-    currentMsg = msg;
-    currentLevel = level;
+    final UserMessageWidget msg = new UserMessageWidget(level, title, message, id, closeable,
+        closeCallback);
 
     // msg.getText().
 
@@ -71,5 +69,10 @@
         }
       }
     });
+    return msg;
   }
+
+  public int getCurrentMsgCount() {
+    return panel.getWidgetCount();
+  }
 }

Modified: trunk/src/main/java/cc/kune/msgs/client/UserMessagesPresenter.java
===================================================================
--- trunk/src/main/java/cc/kune/msgs/client/UserMessagesPresenter.java	2011-12-05 20:42:43 UTC (rev 1633)
+++ trunk/src/main/java/cc/kune/msgs/client/UserMessagesPresenter.java	2011-12-06 01:42:07 UTC (rev 1634)
@@ -23,21 +23,56 @@
 
 public class UserMessagesPresenter {
 
-    public interface UserMessagesView {
-        void add(NotifyLevel level, String title, String message, String id, boolean closable, CloseCallback callback);
-    }
+  public interface UserMessagesView {
+    UserMessage add(NotifyLevel level, String title, String message, String id, Boolean closable,
+        CloseCallback callback);
+  }
 
-    private UserMessagesView view;
+  private boolean currentClosable;
+  private String currentId;
+  private NotifyLevel currentLevel;
+  private UserMessage currentMsg;
+  private String currentTitle;
+  private UserMessagesView view;
 
-    public UserMessagesPresenter() {
-    }
+  public UserMessagesPresenter() {
+  }
 
-    public void add(final NotifyLevel level, final String title, final String message, final String id,
-            final boolean closable, final CloseCallback closeCallback) {
-        view.add(level, title, message, id, closable, closeCallback);
+  public UserMessage add(final NotifyLevel level, final String title, final String message,
+      final String id, final boolean closable, final CloseCallback closeCallback) {
+    if ((currentMsg != null && !currentMsg.isAttached()) || !level.equals(currentLevel)
+        || !same(title, currentTitle) || !same(id, currentId) || closable != currentClosable) {
+      currentMsg = view.add(level, title, message, id, closable, closeCallback);
+    } else {
+      // Similar message, so, I'll reuse the widget
+      assert (currentMsg != null);
+      currentMsg.appendMsg(message);
     }
+    currentLevel = level;
+    currentTitle = title;
+    currentId = id;
+    currentClosable = closable;
+    return currentMsg;
+  }
 
-    public void init(final UserMessagesView view) {
-        this.view = view;
+  public void init(final UserMessagesView view) {
+    this.view = view;
+  }
+
+  private boolean same(final Object object, final Object otherObj) {
+    if (object == otherObj) {
+      return true;
     }
+    if (otherObj == null) {
+      return false;
+    }
+    if (object == null) {
+      if (otherObj != null) {
+        return false;
+      }
+    } else if (!object.equals(otherObj)) {
+      return false;
+    }
+    return true;
+  }
 }

Added: trunk/src/test/java/cc/kune/msgs/client/UserMessagesPresenterTest.java
===================================================================
--- trunk/src/test/java/cc/kune/msgs/client/UserMessagesPresenterTest.java	                        (rev 0)
+++ trunk/src/test/java/cc/kune/msgs/client/UserMessagesPresenterTest.java	2011-12-06 01:42:07 UTC (rev 1634)
@@ -0,0 +1,131 @@
+package cc.kune.msgs.client;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+import cc.kune.common.client.notify.NotifyLevel;
+import cc.kune.msgs.client.UserMessagesPresenter.UserMessagesView;
+
+public class UserMessagesPresenterTest {
+
+  private static final String ID_1 = "id1";
+  private static final String ID_2 = "id2";
+  private static final String MESSAGE_1 = "message 1";
+  private static final String MESSAGE_2 = "message 2";
+  private static final String TITLE_1 = "title 1";
+  private static final String TITLE_2 = "title 2";
+  private CloseCallback closeCallback;
+  private UserMessage msg;
+  private UserMessagesPresenter presenter;
+  private UserMessagesView view;
+
+  @Test
+  public void basicMsg() {
+    presenter.add(NotifyLevel.info, TITLE_1, MESSAGE_1, null, false, closeCallback);
+    Mockito.verify(view, Mockito.times(1)).add(NotifyLevel.info, TITLE_1, MESSAGE_1, null, false,
+        closeCallback);
+  }
+
+  @Before
+  public void before() {
+    presenter = new UserMessagesPresenter();
+    view = Mockito.mock(UserMessagesView.class);
+    closeCallback = Mockito.mock(CloseCallback.class);
+    presenter.init(view);
+    msg = Mockito.mock(UserMessage.class);
+    // Mockito.when(msg.getText()).thenReturn(MESSAGE_1);
+    Mockito.when(
+        view.add((NotifyLevel) Mockito.anyObject(), Mockito.anyString(), Mockito.anyString(),
+            Mockito.anyString(), Mockito.anyBoolean(), (CloseCallback) Mockito.anyObject())).thenReturn(
+        msg);
+  }
+
+  @Test
+  public void twoBasicAvatarMsg() {
+    final NotifyLevel avatar = NotifyLevel.avatar.url("image1.png");
+    presenter.add(avatar, TITLE_1, MESSAGE_1, null, false, closeCallback);
+    Mockito.when(msg.isAttached()).thenReturn(true);
+    presenter.add(avatar, TITLE_1, MESSAGE_2, null, false, closeCallback);
+    Mockito.verify(view, Mockito.times(1)).add(avatar, TITLE_1, MESSAGE_1, null, false, closeCallback);
+    Mockito.verify(msg, Mockito.times(1)).appendMsg(MESSAGE_2);
+  }
+
+  @Test
+  public void twoBasicDiffAvatarMsg() {
+    final NotifyLevel avatar1 = NotifyLevel.avatar.url("image1.png");
+    presenter.add(avatar1, TITLE_1, MESSAGE_1, ID_1, false, closeCallback);
+    final NotifyLevel avatar2 = NotifyLevel.avatar.url("image2.png");
+    presenter.add(avatar2, TITLE_1, MESSAGE_1, ID_1, true, closeCallback);
+    Mockito.verify(view, Mockito.times(1)).add(avatar1, TITLE_1, MESSAGE_1, ID_1, false, closeCallback);
+    Mockito.verify(view, Mockito.times(1)).add(avatar2, TITLE_1, MESSAGE_1, ID_1, true, closeCallback);
+    Mockito.verify(msg, Mockito.never()).appendMsg(MESSAGE_1);
+  }
+
+  @Test
+  public void twoBasicDiffCloseableMsg() {
+    presenter.add(NotifyLevel.info, TITLE_1, MESSAGE_1, ID_1, false, closeCallback);
+    presenter.add(NotifyLevel.info, TITLE_1, MESSAGE_1, ID_1, true, closeCallback);
+    Mockito.verify(view, Mockito.times(1)).add(NotifyLevel.info, TITLE_1, MESSAGE_1, ID_1, false,
+        closeCallback);
+    Mockito.verify(view, Mockito.times(1)).add(NotifyLevel.info, TITLE_1, MESSAGE_1, ID_1, true,
+        closeCallback);
+    Mockito.verify(msg, Mockito.never()).appendMsg(MESSAGE_1);
+  }
+
+  @Test
+  public void twoBasicDiffIdMsg() {
+    presenter.add(NotifyLevel.info, TITLE_1, MESSAGE_1, ID_1, false, closeCallback);
+    presenter.add(NotifyLevel.info, TITLE_1, MESSAGE_1, ID_2, false, closeCallback);
+    Mockito.verify(view, Mockito.times(1)).add(NotifyLevel.info, TITLE_1, MESSAGE_1, ID_1, false,
+        closeCallback);
+    Mockito.verify(view, Mockito.times(1)).add(NotifyLevel.info, TITLE_1, MESSAGE_1, ID_2, false,
+        closeCallback);
+    Mockito.verify(msg, Mockito.never()).appendMsg(MESSAGE_1);
+  }
+
+  @Test
+  public void twoBasicDiffLevelMsg() {
+    presenter.add(NotifyLevel.info, TITLE_1, MESSAGE_1, null, false, closeCallback);
+    presenter.add(NotifyLevel.error, TITLE_1, MESSAGE_2, null, false, closeCallback);
+    Mockito.verify(view, Mockito.times(1)).add(NotifyLevel.info, TITLE_1, MESSAGE_1, null, false,
+        closeCallback);
+    Mockito.verify(view, Mockito.times(1)).add(NotifyLevel.error, TITLE_1, MESSAGE_2, null, false,
+        closeCallback);
+    Mockito.verify(msg, Mockito.never()).appendMsg(MESSAGE_2);
+  }
+
+  @Test
+  public void twoBasicDiffTitleMsg() {
+    presenter.add(NotifyLevel.info, TITLE_1, MESSAGE_1, null, false, closeCallback);
+    presenter.add(NotifyLevel.info, TITLE_2, MESSAGE_1, null, false, closeCallback);
+    Mockito.verify(view, Mockito.times(1)).add(NotifyLevel.info, TITLE_1, MESSAGE_1, null, false,
+        closeCallback);
+    Mockito.verify(view, Mockito.times(1)).add(NotifyLevel.info, TITLE_2, MESSAGE_1, null, false,
+        closeCallback);
+    Mockito.verify(msg, Mockito.never()).appendMsg(MESSAGE_1);
+  }
+
+  @Test
+  public void twoBasicMsg() {
+    presenter.add(NotifyLevel.info, TITLE_1, MESSAGE_1, null, false, closeCallback);
+    Mockito.when(msg.isAttached()).thenReturn(true);
+    presenter.add(NotifyLevel.info, TITLE_1, MESSAGE_2, null, false, closeCallback);
+    Mockito.verify(view, Mockito.times(1)).add(NotifyLevel.info, TITLE_1, MESSAGE_1, null, false,
+        closeCallback);
+    Mockito.verify(msg, Mockito.times(1)).appendMsg(MESSAGE_2);
+  }
+
+  @Test
+  public void twoBasicMsgButAfterFirstClosed() {
+    presenter.add(NotifyLevel.info, TITLE_1, MESSAGE_1, null, false, closeCallback);
+    Mockito.verify(view, Mockito.times(1)).add(NotifyLevel.info, TITLE_1, MESSAGE_1, null, false,
+        closeCallback);
+    presenter.add(NotifyLevel.info, TITLE_1, MESSAGE_2, null, false, closeCallback);
+    Mockito.when(msg.isAttached()).thenReturn(false);
+    Mockito.verify(view, Mockito.times(1)).add(NotifyLevel.info, TITLE_1, MESSAGE_1, null, false,
+        closeCallback);
+    Mockito.verify(msg, Mockito.never()).appendMsg(MESSAGE_2);
+  }
+
+}




More information about the kune-commits mailing list