[kune-commits] r1877 - in trunk/src/main/java/cc/kune: core/client/sn/actions gspace/client/viewers wave/client

Vicente J. Ruiz Jurado vjrj_ at ourproject.org
Wed May 30 12:46:30 CEST 2012


Author: vjrj_
Date: 2012-05-30 12:46:29 +0200 (Wed, 30 May 2012)
New Revision: 1877

Added:
   trunk/src/main/java/cc/kune/wave/client/WaveUnsavedIndicator.java
Modified:
   trunk/src/main/java/cc/kune/core/client/sn/actions/SessionAction.java
   trunk/src/main/java/cc/kune/gspace/client/viewers/ContentViewerPanel.java
   trunk/src/main/java/cc/kune/wave/client/KuneStagesProvider.java
   trunk/src/main/java/cc/kune/wave/client/WebClient.java
Log:
CLOSED - # 278: Implements a custom UnsavedDataListener 
http://kune.ourproject.org/issues/ticket/278

Modified: trunk/src/main/java/cc/kune/core/client/sn/actions/SessionAction.java
===================================================================
--- trunk/src/main/java/cc/kune/core/client/sn/actions/SessionAction.java	2012-05-24 13:19:59 UTC (rev 1876)
+++ trunk/src/main/java/cc/kune/core/client/sn/actions/SessionAction.java	2012-05-30 10:46:29 UTC (rev 1877)
@@ -27,9 +27,22 @@
 
 import com.google.inject.Inject;
 
+/**
+ * The Class SessionAction is used to create actions that depends of current
+ * session auth status
+ */
 public abstract class SessionAction extends AbstractExtendedAction {
+
   protected final Session session;
 
+  /**
+   * Instantiates a new session action.
+   * 
+   * @param session
+   *          the kune session
+   * @param authNeed
+   *          if user authentication is needed
+   */
   @Inject
   public SessionAction(final Session session, final boolean authNeed) {
     this.session = session;
@@ -42,6 +55,14 @@
     });
   }
 
+  /**
+   * Refresh status.
+   * 
+   * @param authNeed
+   *          the auth need
+   * @param isLogged
+   *          the is logged
+   */
   public void refreshStatus(final boolean authNeed, final boolean isLogged) {
     boolean visible = false;
     final boolean noLogged = !isLogged;
@@ -54,6 +75,12 @@
     setVisible(visible);
   }
 
+  /**
+   * Sets the visible.
+   * 
+   * @param visible
+   *          the new visible
+   */
   public void setVisible(final boolean visible) {
     setEnabled(visible);
     putValue(GuiActionDescrip.VISIBLE, visible);

Modified: trunk/src/main/java/cc/kune/gspace/client/viewers/ContentViewerPanel.java
===================================================================
--- trunk/src/main/java/cc/kune/gspace/client/viewers/ContentViewerPanel.java	2012-05-24 13:19:59 UTC (rev 1876)
+++ trunk/src/main/java/cc/kune/gspace/client/viewers/ContentViewerPanel.java	2012-05-30 10:46:29 UTC (rev 1877)
@@ -48,6 +48,7 @@
 import cc.kune.wave.client.WaveClientProvider;
 import cc.kune.wave.client.WaveClientUtils;
 import cc.kune.wave.client.WaveClientView;
+import cc.kune.wave.client.WaveUnsavedIndicator;
 import cc.kune.wave.client.WebClientMock;
 
 import com.google.gwt.core.client.GWT;
@@ -80,12 +81,14 @@
   @UiField
   DeckPanel deck;
   private final ContentDropController dropController;
+  private FramedPanel dummyWaveFrame;
   private final GSpaceArmor gsArmor;
   private final I18nTranslationService i18n;
   private IdGenerator idGenerator;
   private Element loading;
   @UiField
   InlineHTML onlyViewPanel;
+
   private ProfileManager profiles;
 
   private final StateManager stateManager;
@@ -94,27 +97,28 @@
   private KuneStagesProvider wave;
 
   private final WaveClientProvider waveClientProv;
-
   private ImplPanel waveHolder;
   @UiField
   ImplPanel waveHolderParent;
+
   private final WaveStore waveStore = new SimpleWaveStore();
 
+  private final WaveUnsavedIndicator waveUnsavedIndicator;
+
   private final Widget widget;
 
-  private FramedPanel dummyWaveFrame;
-
   @Inject
   public ContentViewerPanel(final GSpaceArmor wsArmor, final WaveClientProvider waveClient,
       final ContentCapabilitiesRegistry capabilitiesRegistry, final I18nTranslationService i18n,
       final EventBus eventBus, final StateManager stateManager,
-      final ContentDropController dropController) {
+      final ContentDropController dropController, final WaveUnsavedIndicator waveUnsavedIndicator) {
     this.gsArmor = wsArmor;
     this.waveClientProv = waveClient;
     this.capabilitiesRegistry = capabilitiesRegistry;
     this.i18n = i18n;
     this.stateManager = stateManager;
     this.dropController = dropController;
+    this.waveUnsavedIndicator = waveUnsavedIndicator;
     widget = uiBinder.createAndBindUi(this);
     contentTitle = new ContentTitleWidget(i18n, gsArmor, capabilitiesRegistry.getIconsRegistry());
     eventBus.addHandler(WaveClientClearEvent.getType(),
@@ -228,9 +232,10 @@
       // UIObject.setVisible(waveFrame.getElement(), true);
       waveHolder.getElement().appendChild(loading);
       final Element holder = waveHolder.getElement().appendChild(Document.get().createDivElement());
-      final KuneStagesProvider wave = new KuneStagesProvider(holder, (com.google.gwt.dom.client.Element) new Label().getElement(), waveHolder, dummyWaveFrame, waveRef, channel,
-          idGenerator, profiles, waveStore, isNewWave,
-          org.waveprotocol.box.webclient.client.Session.get().getDomain(), true, i18n);
+      final KuneStagesProvider wave = new KuneStagesProvider(holder, new Label().getElement(),
+          waveHolder, dummyWaveFrame, waveRef, channel, idGenerator, profiles, waveStore, isNewWave,
+          org.waveprotocol.box.webclient.client.Session.get().getDomain(), true, i18n,
+          waveUnsavedIndicator);
       this.wave = wave;
       wave.load(new Command() {
         @Override

Modified: trunk/src/main/java/cc/kune/wave/client/KuneStagesProvider.java
===================================================================
--- trunk/src/main/java/cc/kune/wave/client/KuneStagesProvider.java	2012-05-24 13:19:59 UTC (rev 1876)
+++ trunk/src/main/java/cc/kune/wave/client/KuneStagesProvider.java	2012-05-30 10:46:29 UTC (rev 1877)
@@ -19,7 +19,6 @@
 package cc.kune.wave.client;
 
 import org.waveprotocol.box.webclient.client.RemoteViewServiceMultiplexer;
-import org.waveprotocol.box.webclient.client.SavedStateIndicator;
 import org.waveprotocol.box.webclient.client.StageTwoProvider;
 import org.waveprotocol.box.webclient.client.WindowTitleHandler;
 import org.waveprotocol.box.webclient.search.WaveContext;
@@ -73,31 +72,46 @@
 
   private final static AsyncHolder<Object> HALT = new AsyncHolder<Object>() {
     @Override
-    public void call(Accessor<Object> accessor) {
+    public void call(final Accessor<Object> accessor) {
       // Never ready, so never notify the accessor.
     }
   };
   
-  private final Element wavePanelElement;
-  private final Element unsavedIndicatorElement;
-  private final FramedPanel waveFrame;
-  private final LogicalPanel rootPanel;
-  private final WaveRef waveRef;
+  /**
+   * Finds the blip that should receive the focus and selects it.
+   */
+  private static void selectAndFocusOnBlip(final Reader reader, final ModelAsViewProvider views,
+      final ConversationView wave, final FocusFramePresenter focusFrame, final WaveRef waveRef) {
+    final FocusBlipSelector blipSelector =
+        FocusBlipSelector.create(wave, views, reader, new ViewTraverser());
+    final BlipView blipUi = blipSelector.selectBlipByWaveRef(waveRef);
+    // Focus on the selected blip.
+    if (blipUi != null) {
+      focusFrame.focus(blipUi);
+    }
+  }
   private final RemoteViewServiceMultiplexer channel;
+  private boolean closed;
+  private final I18nTranslationService i18n;
   private final IdGenerator idGenerator;
-  private final ProfileManager profiles;
-  private final WaveStore waveStore;
   private final boolean isNewWave;
   private final String localDomain;
+  private StageOne one;
+  private final ProfileManager profiles;
+  private final LogicalPanel rootPanel;
+  private final boolean showParticipantsPanel;
 
-  private boolean closed;
-  private StageOne one;
+  private StageThree three;
   private StageTwo two;
-  private StageThree three;
+  private final Element unsavedIndicatorElement;
   private WaveContext wave;
-  private final boolean showParticipantsPanel;
-  private final I18nTranslationService i18n;
+  private final FramedPanel waveFrame;
+  private final Element wavePanelElement;
+  private final WaveRef waveRef;
+  private final WaveStore waveStore;
 
+  private final WaveUnsavedIndicator waveUnsavedIndicator;
+
   /**
    * @param wavePanelElement the DOM element to become the wave panel.
    * @param unsavedIndicatorElement the element that displays the wave saved state.
@@ -110,10 +124,10 @@
    * @param isNewWave true if the wave is a new client-created wave
    * @param idGenerator
    */
-  public KuneStagesProvider(Element wavePanelElement, Element unsavedIndicatorElement,
-      LogicalPanel rootPanel, FramedPanel waveFrame, WaveRef waveRef, RemoteViewServiceMultiplexer channel,
-      IdGenerator idGenerator, ProfileManager profiles, WaveStore store, boolean isNewWave,
-      String localDomain, boolean showParticipantsPanel, I18nTranslationService i18n) {
+  public KuneStagesProvider(final Element wavePanelElement, final Element unsavedIndicatorElement,
+      final LogicalPanel rootPanel, final FramedPanel waveFrame, final WaveRef waveRef, final RemoteViewServiceMultiplexer channel,
+      final IdGenerator idGenerator, final ProfileManager profiles, final WaveStore store, final boolean isNewWave,
+      final String localDomain, final boolean showParticipantsPanel, final I18nTranslationService i18n, final WaveUnsavedIndicator waveUnsavedIndicator) {
     this.wavePanelElement = wavePanelElement;
     this.unsavedIndicatorElement = unsavedIndicatorElement;
     this.waveFrame = waveFrame;
@@ -127,49 +141,25 @@
     this.localDomain = localDomain;
     this.showParticipantsPanel = showParticipantsPanel;
     this.i18n = i18n;
+    this.waveUnsavedIndicator = waveUnsavedIndicator;
   }
 
   @Override
-  protected AsyncHolder<StageZero> createStageZeroLoader() {
-    return haltIfClosed(super.createStageZeroLoader());
-  }
-
-  @Override
-  protected AsyncHolder<StageOne> createStageOneLoader(StageZero zero) {
+  protected AsyncHolder<StageOne> createStageOneLoader(final StageZero zero) {
     return haltIfClosed(new StageOne.DefaultProvider(zero) {
       @Override
-      protected Element createWaveHolder() {
-        return wavePanelElement;
+      protected LogicalPanel createWaveContainer() {
+        return rootPanel;
       }
 
       @Override
-      protected LogicalPanel createWaveContainer() {
-        return rootPanel;
+      protected Element createWaveHolder() {
+        return wavePanelElement;
       }
     });
   }
 
   @Override
-  protected AsyncHolder<StageTwo> createStageTwoLoader(StageOne one) {
-    return haltIfClosed(new StageTwoProvider(this.one = one, waveRef, channel, isNewWave, 
-       idGenerator, profiles, new SavedStateIndicator(unsavedIndicatorElement)) {
-      // Kune patch
-      @Override
-      protected DomRenderer createRenderer() {
-        return KuneFullDomWaveRendererImpl.create(getConversations(), getProfileManager(),
-            getBlipDetailer(), getViewIdMapper(), getBlipQueue(), getThreadReadStateMonitor(),
-            createViewFactories(), showParticipantsPanel);
-        
-        // Warning: this run into issue #73
-        // @Override
-        // protected void installFeatures() {
-        // WavePanelResourceLoader.loadCss();
-        // // KuneWavePanelResourceLoader.loadCss();
-        // }
-
-      }});}
-  
-  @Override
   protected AsyncHolder<StageThree> createStageThreeLoader(final StageTwo two) {
     return haltIfClosed(new StageThree.DefaultProvider(this.two = two) {
       @Override
@@ -177,7 +167,7 @@
         // Prepend an init wave flow onto the stage continuation.
         super.create(new Accessor<StageThree>() {
           @Override
-          public void use(StageThree x) {
+          public void use(final StageThree x) {
             onStageThreeLoaded(x, whenReady);
           }
         });
@@ -216,53 +206,32 @@
       
     });
   }
-
-  private void onStageThreeLoaded(StageThree x, Accessor<StageThree> whenReady) {
-    if (closed) {
-      // Stop the loading process.
-      return;
-    }
-    three = x;
-    if (isNewWave) {
-      initNewWave(x);
-    } else {
-      handleExistingWave(x);
-    }
-    wave = new WaveContext(
-				two.getWave(), two.getConversations(), two.getSupplement(), two.getReadMonitor());
-    waveStore.add(wave);
-    install();
-    whenReady.use(x);
-  }
   
-  private void initNewWave(StageThree three) {
-    // Do the new-wave flow.
-    ModelAsViewProvider views = two.getModelAsViewProvider();
-    BlipQueueRenderer blipQueue = two.getBlipQueue();
-    ConversationView wave = two.getConversations();
+  @Override
+  protected AsyncHolder<StageTwo> createStageTwoLoader(final StageOne one) {
+    return haltIfClosed(new StageTwoProvider(this.one = one, waveRef, channel, isNewWave, 
+       idGenerator, profiles, waveUnsavedIndicator) {
+      // Kune patch
+      @Override
+      protected DomRenderer createRenderer() {
+        return KuneFullDomWaveRendererImpl.create(getConversations(), getProfileManager(),
+            getBlipDetailer(), getViewIdMapper(), getBlipQueue(), getThreadReadStateMonitor(),
+            createViewFactories(), showParticipantsPanel);
+        
+        // Warning: this run into issue #73
+        // @Override
+        // protected void installFeatures() {
+        // WavePanelResourceLoader.loadCss();
+        // // KuneWavePanelResourceLoader.loadCss();
+        // }
 
-    // Force rendering to finish.
-    blipQueue.flush();
-    BlipView blipUi = views.getBlipView(wave.getRoot().getRootThread().getFirstBlip());
-    three.getEditActions().startEditing(blipUi);
-  }
+      }});}
 
-  private void handleExistingWave(StageThree three) {
-  if (waveRef.hasDocumentId()) {
-    BlipQueueRenderer blipQueue = two.getBlipQueue();
-    blipQueue.flush();
-    selectAndFocusOnBlip(two.getReader(), two.getModelAsViewProvider(), two.getConversations(),
-        one.getFocusFrame(), waveRef);
+  @Override
+  protected AsyncHolder<StageZero> createStageZeroLoader() {
+    return haltIfClosed(super.createStageZeroLoader());
   }
-  }
-
-  /**
- * A hook to install features that are not dependent an a certain stage.
- */
-  protected void install() {
-  WindowTitleHandler.install(waveStore, waveFrame);
-  }
-
+  
   public void destroy() {
     if (wave != null) {
       waveStore.remove(wave);
@@ -294,26 +263,58 @@
     }
     closed = true;
   }
-  /**
-   * Finds the blip that should receive the focus and selects it.
-   */
-  private static void selectAndFocusOnBlip(Reader reader, ModelAsViewProvider views,
-      ConversationView wave, FocusFramePresenter focusFrame, WaveRef waveRef) {
-    FocusBlipSelector blipSelector =
-        FocusBlipSelector.create(wave, views, reader, new ViewTraverser());
-    BlipView blipUi = blipSelector.selectBlipByWaveRef(waveRef);
-    // Focus on the selected blip.
-    if (blipUi != null) {
-      focusFrame.focus(blipUi);
-    }
-  }
 
   /**
    * @return a halting provider if this stage is closed. Otherwise, returns the
    *         given provider.
    */
   @SuppressWarnings("unchecked") // HALT is safe as a holder for any type
-  private <T> AsyncHolder<T> haltIfClosed(AsyncHolder<T> provider) {
+  private <T> AsyncHolder<T> haltIfClosed(final AsyncHolder<T> provider) {
     return closed ? (AsyncHolder<T>) HALT : provider;
   }
+
+  private void handleExistingWave(final StageThree three) {
+  if (waveRef.hasDocumentId()) {
+    final BlipQueueRenderer blipQueue = two.getBlipQueue();
+    blipQueue.flush();
+    selectAndFocusOnBlip(two.getReader(), two.getModelAsViewProvider(), two.getConversations(),
+        one.getFocusFrame(), waveRef);
+  }
+  }
+
+  private void initNewWave(final StageThree three) {
+    // Do the new-wave flow.
+    final ModelAsViewProvider views = two.getModelAsViewProvider();
+    final BlipQueueRenderer blipQueue = two.getBlipQueue();
+    final ConversationView wave = two.getConversations();
+
+    // Force rendering to finish.
+    blipQueue.flush();
+    final BlipView blipUi = views.getBlipView(wave.getRoot().getRootThread().getFirstBlip());
+    three.getEditActions().startEditing(blipUi);
+  }
+  /**
+ * A hook to install features that are not dependent an a certain stage.
+ */
+  protected void install() {
+  WindowTitleHandler.install(waveStore, waveFrame);
+  }
+
+  private void onStageThreeLoaded(final StageThree x, final Accessor<StageThree> whenReady) {
+    if (closed) {
+      // Stop the loading process.
+      return;
+    }
+    three = x;
+    if (isNewWave) {
+      initNewWave(x);
+    } else {
+      handleExistingWave(x);
+    }
+    wave = new WaveContext(
+				two.getWave(), two.getConversations(), two.getSupplement(), two.getReadMonitor());
+    waveStore.add(wave);
+    install();
+    whenReady.use(x);
+  }
 }

Added: trunk/src/main/java/cc/kune/wave/client/WaveUnsavedIndicator.java
===================================================================
--- trunk/src/main/java/cc/kune/wave/client/WaveUnsavedIndicator.java	                        (rev 0)
+++ trunk/src/main/java/cc/kune/wave/client/WaveUnsavedIndicator.java	2012-05-30 10:46:29 UTC (rev 1877)
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2011 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cc.kune.wave.client;
+
+import org.waveprotocol.wave.client.scheduler.Scheduler;
+import org.waveprotocol.wave.client.scheduler.SchedulerInstance;
+import org.waveprotocol.wave.client.scheduler.TimerService;
+import org.waveprotocol.wave.concurrencycontrol.common.UnsavedDataListener;
+
+import cc.kune.common.client.notify.NotifyUser;
+import cc.kune.core.client.i18n.I18n;
+
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+
+ at Singleton
+public class WaveUnsavedIndicator implements UnsavedDataListener {
+
+  private enum SavedState {
+    SAVED, UNSAVED;
+  }
+
+  private static final int UPDATE_DELAY_MS = 300;
+
+  private SavedState currentSavedState = null;
+
+  private final TimerService scheduler;
+
+  private final Scheduler.Task updateTask = new Scheduler.Task() {
+    @Override
+    public void execute() {
+      updateDisplay();
+    }
+  };
+
+  private SavedState visibleSavedState = SavedState.SAVED;
+
+  /**
+   * Simple saved state indicator.
+   * 
+   * @author danilatos at google.com (Daniel Danilatos)
+   * @author yurize at apache.org (Yuri Zelikov)
+   */
+  @Inject
+  public WaveUnsavedIndicator() {
+    this.scheduler = SchedulerInstance.getLowPriorityTimer();
+  }
+
+  private void maybeUpdateDisplay() {
+    if (needsUpdating()) {
+      switch (currentSavedState) {
+      case SAVED:
+        scheduler.scheduleDelayed(updateTask, UPDATE_DELAY_MS);
+        break;
+      case UNSAVED:
+        updateDisplay();
+        break;
+      default:
+        throw new AssertionError("unknown " + currentSavedState);
+      }
+    } else {
+      scheduler.cancel(updateTask);
+    }
+  }
+
+  private boolean needsUpdating() {
+    return visibleSavedState != currentSavedState;
+  }
+
+  @Override
+  public void onClose(final boolean everythingCommitted) {
+    if (everythingCommitted) {
+      saved();
+    } else {
+      unsaved();
+    }
+  }
+
+  @Override
+  public void onUpdate(final UnsavedDataInfo unsavedDataInfo) {
+    if (unsavedDataInfo.estimateUnacknowledgedSize() != 0) {
+      currentSavedState = SavedState.UNSAVED;
+      unsaved();
+    } else {
+      currentSavedState = SavedState.SAVED;
+      saved();
+    }
+  }
+
+  public void saved() {
+    maybeUpdateDisplay();
+  }
+
+  public void unsaved() {
+    maybeUpdateDisplay();
+  }
+
+  private void updateDisplay() {
+    visibleSavedState = currentSavedState;
+    switch (visibleSavedState) {
+    case SAVED:
+      NotifyUser.hideProgress();
+      break;
+    case UNSAVED:
+      NotifyUser.showProgress(I18n.t("Saving"));
+      break;
+    default:
+      throw new AssertionError("unknown " + currentSavedState);
+    }
+  }
+
+}

Modified: trunk/src/main/java/cc/kune/wave/client/WebClient.java
===================================================================
--- trunk/src/main/java/cc/kune/wave/client/WebClient.java	2012-05-24 13:19:59 UTC (rev 1876)
+++ trunk/src/main/java/cc/kune/wave/client/WebClient.java	2012-05-30 10:46:29 UTC (rev 1877)
@@ -106,27 +106,126 @@
   interface Binder extends UiBinder<DockLayoutPanel, WebClient> {
   }
 
+  /**
+   * An exception handler that reports exceptions using a <em>shiny banner</em>
+   * (an alert placed on the top of the screen). Once the stack trace is
+   * prepared, it is revealed in the banner via a link.
+   */
+  public static class ErrorHandler implements UncaughtExceptionHandler {
+    public static void getStackTraceAsync(final Throwable t, final Accessor<SafeHtml> whenReady) {
+      // TODO: Request stack-trace de-obfuscation. For now, just use the
+      // javascript stack trace.
+      //
+      // Use minimal services here, in order to avoid the chance that reporting
+      // the error produces more errors. In particular, do not use WIAB's
+      // scheduler to run this command.
+      // Also, this code could potentially be put behind a runAsync boundary, to
+      // save whatever dependencies it uses from the initial download.
+      new Timer() {
+        @Override
+        public void run() {
+          final SafeHtmlBuilder stack = new SafeHtmlBuilder();
+
+          Throwable error = t;
+          while (error != null) {
+            final String token = String.valueOf((new Date()).getTime());
+            stack.appendHtmlConstant("Token:  " + token + "<br> ");
+            stack.appendEscaped(String.valueOf(error.getMessage())).appendHtmlConstant("<br>");
+            for (final StackTraceElement elt : error.getStackTrace()) {
+              stack.appendHtmlConstant("  ")
+                  .appendEscaped(maybe(elt.getClassName(), "??")).appendHtmlConstant(".") //
+                  .appendEscaped(maybe(elt.getMethodName(), "??")).appendHtmlConstant(" (") //
+                  .appendEscaped(maybe(elt.getFileName(), "??")).appendHtmlConstant(":") //
+                  .appendEscaped(maybe(elt.getLineNumber(), "??")).appendHtmlConstant(")") //
+                  .appendHtmlConstant("<br>");
+            }
+            error = error.getCause();
+            if (error != null) {
+              stack.appendHtmlConstant("Caused by: ");
+            }
+          }
+
+          whenReady.use(stack.toSafeHtml());
+        }
+      }.schedule(1);
+    }
+
+    public static void install() {
+    GWT.setUncaughtExceptionHandler(new ErrorHandler(GWT.getUncaughtExceptionHandler()));
+    }
+
+    private static String maybe(final int value, final String otherwise) {
+      return value != -1 ? String.valueOf(value) : otherwise;
+    }
+
+    private static String maybe(final String value, final String otherwise) {
+      return value != null ? value : otherwise;
+    }
+
+    /**
+     * Indicates whether an error has already been reported (at most one error
+     * is ever reported by this handler).
+     */
+    private boolean hasFired;
+
+    /** Next handler in the handler chain. */
+    private final UncaughtExceptionHandler next;
+
+    private ErrorHandler(final UncaughtExceptionHandler next) {
+      this.next = next;
+    }
+
+    @Override
+    public void onUncaughtException(final Throwable e) {
+      if (!hasFired) {
+        hasFired = true;
+      //  final ErrorIndicatorPresenter error =
+        //    ErrorIndicatorPresenter.create(RootPanel.get("banner"));
+        getStackTraceAsync(e, new Accessor<SafeHtml>() {
+          @Override
+          public void use(final SafeHtml stack) {
+          //  error.addDetail(stack, null);
+            // REMOTE_LOG.severe(stack.asString().replace("<br>", "\n"));
+            final String message = stack.asString().replace("<br>", "\n");
+            REMOTE_LOG.severe(message);
+            NotifyUser.logError(message);
+            NotifyUser.showProgress("Error");
+            new Timer() {
+              @Override
+              public void run() {
+                NotifyUser.hideProgress();
+              }}.schedule(5000);
+          }
+        });
+      }
+
+      if (next != null) {
+        next.onUncaughtException(e);
+      }
+    }
+  }
+
   interface Style extends CssResource {
   }
 
   private static final Binder BINDER = GWT.create(Binder.class);
+  static Log LOG = Log.get(WebClient.class);
 
-  static Log LOG = Log.get(WebClient.class);
   // Use of GWT logging is only intended for sending exception reports to the
   // server, nothing else in the client should use java.util.logging.
   // Please also see WebClientDemo.gwt.xml.
   private static final Logger REMOTE_LOG = Logger.getLogger("REMOTE_LOG");
-
   /** Creates a popup that warns about network disconnects. */
   private static UniversalPopup createTurbulencePopup() {
-    PopupChrome chrome = PopupChromeFactory.createPopupChrome();
-    UniversalPopup popup =
+    final PopupChrome chrome = PopupChromeFactory.createPopupChrome();
+    final UniversalPopup popup =
         PopupFactory.createPopup(null, new CenterPopupPositioner(), chrome, true);
     popup.add(new HTML("<div style='color: red; padding: 5px; text-align: center;'>"
         + "<b>A turbulence detected!<br></br>"
         + " Please save your last changes to somewhere and reload the wave.</b></div>"));
     return popup;
   }
+
   private RemoteViewServiceMultiplexer channel;
 
   private final EventBus eventBus;
@@ -136,8 +235,8 @@
   private IdGenerator idGenerator;
 
   private final InboxCountPresenter inboxCount;
+  private final Element loading = new LoadingIndicator().getElement();
 
-  private final Element loading = new LoadingIndicator().getElement();
   private ParticipantId loggedInUser;
 
   @UiField
@@ -148,10 +247,10 @@
   @UiField(provided = true)
   final SearchPanelWidget searchPanel;
 
+
   @UiField
   SplitLayoutPanel splitPanel;
 
-
   @UiField
   Style style;
 
@@ -162,9 +261,11 @@
 
   @UiField
   FramedPanel waveFrame;
-
   ImplPanel waveHolder;
   private final WaveStore waveStore = new SimpleWaveStore();
+
+  private final WaveUnsavedIndicator waveUnsavedIndicator;
+
   /**
    * Create a remote websocket to talk to the server-side FedOne service.
    */
@@ -174,11 +275,12 @@
    * This is the entry point method.
    */
   @Inject
-  public WebClient(final EventBus eventBus, final KuneWaveProfileManager profiles, final InboxCountPresenter inboxCount, final TokenMatcher tokenMatcher, final cc.kune.core.client.state.Session session, final I18nTranslationService i18n) {
+  public WebClient(final EventBus eventBus, final KuneWaveProfileManager profiles, final InboxCountPresenter inboxCount, final TokenMatcher tokenMatcher, final cc.kune.core.client.state.Session session, final I18nTranslationService i18n, final WaveUnsavedIndicator waveUnsavedIndicator) {
     this.eventBus = eventBus;
     this.profiles = profiles;
     this.inboxCount = inboxCount;
     this.i18n = i18n;
+    this.waveUnsavedIndicator = waveUnsavedIndicator;
     searchPanel = new SearchPanelWidget(new SearchPanelRenderer(profiles));
     ErrorHandler.install();
     eventBus.addHandler(StackErrorEvent.getType(), new StackErrorEvent.StackErrorHandler() {
@@ -268,12 +370,27 @@
   public void getStackTraceAsync(final Throwable caught, final Accessor<SafeHtml> accessor) {
     ErrorHandler.getStackTraceAsync(caught, accessor);
   }
-
   @Override
   public ImplPanel getWaveHolder() {
     return waveHolder;
   }
+
   @Override
+  public WaveWebSocketClient getWebSocket() {
+    return websocket;
+  }
+
+
+  /**
+   * Returns <code>ws(s)://yourhost[:port]/</code>.
+   */
+  // XXX check formatting wrt GPE
+  private native String getWebSocketBaseUrl(String moduleBase) /*-{
+		return ((window.location.protocol == "https:") ? "wss" : "ws")
+				+ /:\/\/[^\/]+/.exec(moduleBase)[0] + "/";
+  }-*/;
+
+  @Override
   public void login() {
     loginImpl();
   }
@@ -287,6 +404,12 @@
     }
   }
 
+  /**
+   */
+  private void loginToServer() {
+    assert loggedInUser != null;
+    channel = new RemoteViewServiceMultiplexer(websocket, loggedInUser.getAddress());
+  }
 
   @Override
   public void logout() {
@@ -297,130 +420,13 @@
     clear();
   }
 
-  @Override
-  public void setMaximized(final boolean maximized) {
-    splitPanel.setWidgetSize(searchPanel, maximized ? 0 : 400);    
-  }
-
-  private void setupUi() {
-    // Set up UI
-    DockLayoutPanel self = BINDER.createAndBindUi(this);
-    // kune-patch
-    // RootPanel.get("app").add(self);
-    initWidget(self);
-    waveHolder = new ImplPanel("");
-    waveHolder.addStyleName("k-waveHolder");
-    waveFrame.add(waveHolder);
-    // DockLayoutPanel forcibly conflicts with sensible layout control, and
-    // sticks inline styles on elements without permission. They must be
-    // cleared.
-    self.getElement().getStyle().clearPosition();
-    splitPanel.setWidgetMinSize(searchPanel, 300);
-  
-    if (LogLevel.showDebug()) {
-      logPanel.enable();
-    } else {
-      logPanel.removeFromParent();
-    }
-  
-    setupSearchPanel();
-    setupWavePanel();
-  }
-
-  @Override
-  public WaveWebSocketClient getWebSocket() {
-    return websocket;
-  }
-
-  private void setupSearchPanel() {
-    // On wave action fire an event.
-    SearchPresenter.WaveActionHandler actionHandler =
-        new SearchPresenter.WaveActionHandler() {
-          @Override
-          public void onCreateWave() {
-            ClientEvents.get().fireEvent(WaveCreationEvent.CREATE_NEW_WAVE);
-          }
-
-          @Override
-          public void onWaveSelected(WaveId id) {
-            ClientEvents.get().fireEvent(new WaveSelectionEvent(WaveRef.of(id)));
-          }
-        };
-    Search search = SimpleSearch.create(RemoteSearchService.create(), waveStore);
-    search.addListener(inboxCount.getSearchListener());
-    SearchPresenter.create(search, searchPanel, actionHandler, profiles);
-  }
-
-  private void setupWavePanel() {
-    // Hide the frame until waves start getting opened.
-    UIObject.setVisible(waveFrame.getElement(), false);
-
-    // Handles opening waves.
-    ClientEvents.get().addWaveSelectionEventHandler(new WaveSelectionEventHandler() {
-      @Override
-      public void onSelection(WaveRef waveRef) {
-        openWave(waveRef, false);
-      }
-    });
-  }
-  
-  private void setupConnectionIndicator() {
-    ClientEvents.get().addNetworkStatusEventHandler(new NetworkStatusEventHandler() {
-
-      boolean isTurbulenceDetected = false;
-
-      @Override
-      public void onNetworkStatus(NetworkStatusEvent event) {
-        Element element = Document.get().getElementById("netstatus");
-        if (element != null) {
-          switch (event.getStatus()) {
-            case CONNECTED:
-            case RECONNECTED:
-              element.setInnerText("Online");
-              element.setClassName("online");
-              isTurbulenceDetected = false;
-              turbulencePopup.hide();
-              break;
-            case DISCONNECTED:
-              element.setInnerText("Offline");
-              element.setClassName("offline");
-              if (!isTurbulenceDetected) {
-                isTurbulenceDetected = true;
-                turbulencePopup.show();
-              }
-              break;
-            case RECONNECTING:
-              element.setInnerText("Connecting...");
-              element.setClassName("connecting");
-              break;
-          }
-        }
-      }
-    });
-  }
-
   /**
-   * Returns <code>ws(s)://yourhost[:port]/</code>.
-   */
-  // XXX check formatting wrt GPE
-  private native String getWebSocketBaseUrl(String moduleBase) /*-{return ((window.location.protocol == "https:") ? "wss" : "ws") + /:\/\/[^\/]+/.exec(moduleBase)[0] + "/";}-*/;
-  
-  private native boolean useSocketIO() /*-{ return !window.WebSocket }-*/;
-  
-  /**
-   */
-  private void loginToServer() {
-    assert loggedInUser != null;
-    channel = new RemoteViewServiceMultiplexer(websocket, loggedInUser.getAddress());
-  }
-
-  /**
    * Shows a wave in a wave panel.
    *
    * @param waveRef wave id to open
    * @param isNewWave whether the wave is being created by this client session.
    */
-  private void openWave(WaveRef waveRef, boolean isNewWave) {
+  private void openWave(final WaveRef waveRef, final boolean isNewWave) {
     LOG.info("WebClient.openWave()");
 
     WaveClientClearEvent.fire(eventBus);
@@ -430,9 +436,9 @@
     // Release the display:none.
     UIObject.setVisible(waveFrame.getElement(), true);
     waveHolder.getElement().appendChild(loading);
-    Element holder = waveHolder.getElement().appendChild(Document.get().createDivElement());
-    KuneStagesProvider wave = new KuneStagesProvider(
-        holder, (com.google.gwt.dom.client.Element) new Label().getElement(), waveHolder, waveFrame, waveRef, channel, idGenerator, profiles, waveStore, isNewWave, Session.get().getDomain(), true, i18n);
+    final Element holder = waveHolder.getElement().appendChild(Document.get().createDivElement());
+    final KuneStagesProvider wave = new KuneStagesProvider(
+        holder, new Label().getElement(), waveHolder, waveFrame, waveRef, channel, idGenerator, profiles, waveStore, isNewWave, Session.get().getDomain(), true, i18n, waveUnsavedIndicator);
     this.wave = wave;
     wave.load(new Command() {
       @Override
@@ -446,7 +452,7 @@
       WaveRef fromWaveRef;
       try {
         fromWaveRef = GwtWaverefEncoder.decodeWaveRefFromPath(encodedToken);
-      } catch (InvalidWaveRefException e) {
+      } catch (final InvalidWaveRefException e) {
         LOG.info("History token contains invalid path: " + encodedToken);
         return;
       }
@@ -461,102 +467,104 @@
     SpaceConfEvent.fire(eventBus, Space.userSpace, tokenFromWaveref);
     History.newItem(tokenFromWaveref, false);
   }
-  /**
-   * An exception handler that reports exceptions using a <em>shiny banner</em>
-   * (an alert placed on the top of the screen). Once the stack trace is
-   * prepared, it is revealed in the banner via a link.
-   */
-  public static class ErrorHandler implements UncaughtExceptionHandler {
-    public static void getStackTraceAsync(final Throwable t, final Accessor<SafeHtml> whenReady) {
-      // TODO: Request stack-trace de-obfuscation. For now, just use the
-      // javascript stack trace.
-      //
-      // Use minimal services here, in order to avoid the chance that reporting
-      // the error produces more errors. In particular, do not use WIAB's
-      // scheduler to run this command.
-      // Also, this code could potentially be put behind a runAsync boundary, to
-      // save whatever dependencies it uses from the initial download.
-      new Timer() {
-        @Override
-        public void run() {
-          SafeHtmlBuilder stack = new SafeHtmlBuilder();
+  
+  @Override
+  public void setMaximized(final boolean maximized) {
+    splitPanel.setWidgetSize(searchPanel, maximized ? 0 : 400);    
+  }
 
-          Throwable error = t;
-          while (error != null) {
-            String token = String.valueOf((new Date()).getTime());
-            stack.appendHtmlConstant("Token:  " + token + "<br> ");
-            stack.appendEscaped(String.valueOf(error.getMessage())).appendHtmlConstant("<br>");
-            for (StackTraceElement elt : error.getStackTrace()) {
-              stack.appendHtmlConstant("  ")
-                  .appendEscaped(maybe(elt.getClassName(), "??")).appendHtmlConstant(".") //
-                  .appendEscaped(maybe(elt.getMethodName(), "??")).appendHtmlConstant(" (") //
-                  .appendEscaped(maybe(elt.getFileName(), "??")).appendHtmlConstant(":") //
-                  .appendEscaped(maybe(elt.getLineNumber(), "??")).appendHtmlConstant(")") //
-                  .appendHtmlConstant("<br>");
-            }
-            error = error.getCause();
-            if (error != null) {
-              stack.appendHtmlConstant("Caused by: ");
-            }
+  private void setupConnectionIndicator() {
+    ClientEvents.get().addNetworkStatusEventHandler(new NetworkStatusEventHandler() {
+
+      boolean isTurbulenceDetected = false;
+
+      @Override
+      public void onNetworkStatus(final NetworkStatusEvent event) {
+        final Element element = Document.get().getElementById("netstatus");
+        if (element != null) {
+          switch (event.getStatus()) {
+            case CONNECTED:
+            case RECONNECTED:
+              element.setInnerText("Online");
+              element.setClassName("online");
+              isTurbulenceDetected = false;
+              turbulencePopup.hide();
+              break;
+            case DISCONNECTED:
+              element.setInnerText("Offline");
+              element.setClassName("offline");
+              if (!isTurbulenceDetected) {
+                isTurbulenceDetected = true;
+                turbulencePopup.show();
+              }
+              break;
+            case RECONNECTING:
+              element.setInnerText("Connecting...");
+              element.setClassName("connecting");
+              break;
           }
-
-          whenReady.use(stack.toSafeHtml());
         }
-      }.schedule(1);
-    }
+      }
+    });
+  }
+  
+  private void setupSearchPanel() {
+    // On wave action fire an event.
+    final SearchPresenter.WaveActionHandler actionHandler =
+        new SearchPresenter.WaveActionHandler() {
+          @Override
+          public void onCreateWave() {
+            ClientEvents.get().fireEvent(WaveCreationEvent.CREATE_NEW_WAVE);
+          }
 
-    public static void install() {
-    GWT.setUncaughtExceptionHandler(new ErrorHandler(GWT.getUncaughtExceptionHandler()));
+          @Override
+          public void onWaveSelected(final WaveId id) {
+            ClientEvents.get().fireEvent(new WaveSelectionEvent(WaveRef.of(id)));
+          }
+        };
+    final Search search = SimpleSearch.create(RemoteSearchService.create(), waveStore);
+    search.addListener(inboxCount.getSearchListener());
+    SearchPresenter.create(search, searchPanel, actionHandler, profiles);
+  }
+  
+  private void setupUi() {
+    // Set up UI
+    final DockLayoutPanel self = BINDER.createAndBindUi(this);
+    // kune-patch
+    // RootPanel.get("app").add(self);
+    initWidget(self);
+    waveHolder = new ImplPanel("");
+    waveHolder.addStyleName("k-waveHolder");
+    waveFrame.add(waveHolder);
+    // DockLayoutPanel forcibly conflicts with sensible layout control, and
+    // sticks inline styles on elements without permission. They must be
+    // cleared.
+    self.getElement().getStyle().clearPosition();
+    splitPanel.setWidgetMinSize(searchPanel, 300);
+  
+    if (LogLevel.showDebug()) {
+      logPanel.enable();
+    } else {
+      logPanel.removeFromParent();
     }
+  
+    setupSearchPanel();
+    setupWavePanel();
+  }
 
-    private static String maybe(final int value, final String otherwise) {
-      return value != -1 ? String.valueOf(value) : otherwise;
-    }
+  private void setupWavePanel() {
+    // Hide the frame until waves start getting opened.
+    UIObject.setVisible(waveFrame.getElement(), false);
 
-    private static String maybe(final String value, final String otherwise) {
-      return value != null ? value : otherwise;
-    }
-
-    /**
-     * Indicates whether an error has already been reported (at most one error
-     * is ever reported by this handler).
-     */
-    private boolean hasFired;
-
-    /** Next handler in the handler chain. */
-    private final UncaughtExceptionHandler next;
-
-    private ErrorHandler(final UncaughtExceptionHandler next) {
-      this.next = next;
-    }
-
-    @Override
-    public void onUncaughtException(final Throwable e) {
-      if (!hasFired) {
-        hasFired = true;
-      //  final ErrorIndicatorPresenter error =
-        //    ErrorIndicatorPresenter.create(RootPanel.get("banner"));
-        getStackTraceAsync(e, new Accessor<SafeHtml>() {
-          @Override
-          public void use(final SafeHtml stack) {
-          //  error.addDetail(stack, null);
-            // REMOTE_LOG.severe(stack.asString().replace("<br>", "\n"));
-            final String message = stack.asString().replace("<br>", "\n");
-            REMOTE_LOG.severe(message);
-            NotifyUser.logError(message);
-            NotifyUser.showProgress("Error");
-            new Timer() {
-              @Override
-              public void run() {
-                NotifyUser.hideProgress();
-              }}.schedule(5000);
-          }
-        });
+    // Handles opening waves.
+    ClientEvents.get().addWaveSelectionEventHandler(new WaveSelectionEventHandler() {
+      @Override
+      public void onSelection(final WaveRef waveRef) {
+        openWave(waveRef, false);
       }
-
-      if (next != null) {
-        next.onUncaughtException(e);
-      }
-    }
+    });
   }
+  private native boolean useSocketIO() /*-{
+		return !window.WebSocket
+  }-*/;
 }




More information about the kune-commits mailing list