Author: yurize
Date: Tue May  1 19:14:21 2012
New Revision: 1332787

URL: http://svn.apache.org/viewvc?rev=1332787&view=rev
Log:
Adds saved state indicator.
https://reviews.apache.org/r/4937/

Added:
    
incubator/wave/trunk/src/org/waveprotocol/box/webclient/client/SavedStateIndicator.java
Modified:
    incubator/wave/trunk/src/org/waveprotocol/box/server/gxp/TopBar.gxp
    
incubator/wave/trunk/src/org/waveprotocol/box/webclient/client/StageTwoProvider.java
    
incubator/wave/trunk/src/org/waveprotocol/box/webclient/client/StagesProvider.java
    
incubator/wave/trunk/src/org/waveprotocol/box/webclient/client/WebClient.java
    incubator/wave/trunk/src/org/waveprotocol/wave/client/StageTwo.java

Modified: incubator/wave/trunk/src/org/waveprotocol/box/server/gxp/TopBar.gxp
URL: 
http://svn.apache.org/viewvc/incubator/wave/trunk/src/org/waveprotocol/box/server/gxp/TopBar.gxp?rev=1332787&r1=1332786&r2=1332787&view=diff
==============================================================================
--- incubator/wave/trunk/src/org/waveprotocol/box/server/gxp/TopBar.gxp 
(original)
+++ incubator/wave/trunk/src/org/waveprotocol/box/server/gxp/TopBar.gxp Tue May 
 1 19:14:21 2012
@@ -39,7 +39,8 @@
       </gxp:if>
       <gxp:if cond='username != null'>
         <gxp:eval expr='username' /><span class="domain">@<gxp:eval 
expr='domain' /></span> |
-        <span id="netstatus" class="offline">Offline</span> | 
+        <span id='unsavedStateContainer' style="width: 60px">Saved</span> |
+        <span id="netstatus" class="offline">Offline</span> |
         <a href="/auth/signout?r=/">Sign out</a>
       </gxp:if>
     </div>

Added: 
incubator/wave/trunk/src/org/waveprotocol/box/webclient/client/SavedStateIndicator.java
URL: 
http://svn.apache.org/viewvc/incubator/wave/trunk/src/org/waveprotocol/box/webclient/client/SavedStateIndicator.java?rev=1332787&view=auto
==============================================================================
--- 
incubator/wave/trunk/src/org/waveprotocol/box/webclient/client/SavedStateIndicator.java
 (added)
+++ 
incubator/wave/trunk/src/org/waveprotocol/box/webclient/client/SavedStateIndicator.java
 Tue May  1 19:14:21 2012
@@ -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 org.waveprotocol.box.webclient.client;
+
+import com.google.gwt.dom.client.Element;
+
+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;
+
+/**
+ * Simple saved state indicator.
+ *
+ * @author [email protected] (Daniel Danilatos)
+ * @author [email protected] (Yuri Zelikov)
+ */
+public class SavedStateIndicator implements UnsavedDataListener {
+
+  private enum SavedState {
+    SAVED("Saved"),
+    UNSAVED("Unsaved...");
+
+    final String message;
+
+    private SavedState(String message) {
+      this.message = message;
+    }
+  }
+
+  private static final int UPDATE_DELAY_MS = 300;
+
+  private final Scheduler.Task updateTask = new Scheduler.Task() {
+    @Override
+    public void execute() {
+      updateDisplay();
+    }
+  };
+
+  private final Element element;
+  private final TimerService scheduler;
+
+  private SavedState visibleSavedState = SavedState.SAVED;
+  private SavedState currentSavedState = null;
+
+  private static final String UNSAVED_HTML =
+      "<span style='color: red; text-align: center;'>" + 
SavedState.UNSAVED.message
+          + "</span>";
+  private static final String SAVED_HTML =
+      "<span style='color: green; text-align: center;'>" + 
SavedState.SAVED.message
+          + "</span>";
+
+  public SavedStateIndicator(Element element) {
+    this.element = element;
+    this.scheduler = SchedulerInstance.getLowPriorityTimer();
+  }
+
+  public void saved() {
+    maybeUpdateDisplay();
+  }
+
+  public void unsaved() {
+    maybeUpdateDisplay();
+  }
+
+  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;
+  }
+
+  private void updateDisplay() {
+    visibleSavedState = currentSavedState;
+    String innerHtml = visibleSavedState == SavedState.SAVED ? SAVED_HTML : 
UNSAVED_HTML;
+    element.setInnerHTML(innerHtml);
+  }
+
+  @Override
+  public void onUpdate(UnsavedDataInfo unsavedDataInfo) {
+    if (unsavedDataInfo.estimateUnacknowledgedSize() != 0) {
+      currentSavedState = SavedState.UNSAVED;
+      unsaved();
+    } else {
+      currentSavedState = SavedState.SAVED;
+      saved();
+    }
+  }
+
+  @Override
+  public void onClose(boolean everythingCommitted) {
+    if (everythingCommitted) {
+      saved();
+    } else {
+      unsaved();
+    }
+  }
+}

Modified: 
incubator/wave/trunk/src/org/waveprotocol/box/webclient/client/StageTwoProvider.java
URL: 
http://svn.apache.org/viewvc/incubator/wave/trunk/src/org/waveprotocol/box/webclient/client/StageTwoProvider.java?rev=1332787&r1=1332786&r2=1332787&view=diff
==============================================================================
--- 
incubator/wave/trunk/src/org/waveprotocol/box/webclient/client/StageTwoProvider.java
 (original)
+++ 
incubator/wave/trunk/src/org/waveprotocol/box/webclient/client/StageTwoProvider.java
 Tue May  1 19:14:21 2012
@@ -19,6 +19,7 @@
 package org.waveprotocol.box.webclient.client;
 
 import com.google.common.base.Preconditions;
+import com.google.gwt.dom.client.Element;
 import com.google.gwt.user.client.Command;
 
 import org.waveprotocol.wave.client.StageOne;
@@ -60,10 +61,12 @@ public class StageTwoProvider extends St
    * @param waveId the id of the wave to open, or null to create a new wave
    * @param channel communication channel
    * @param idGenerator
+   * @param unsavedIndicatorElement
    */
   public StageTwoProvider(StageOne stageOne, WaveRef waveRef, 
RemoteViewServiceMultiplexer channel,
-      boolean isNewWave, IdGenerator idGenerator, ProfileManager profiles) {
-    super(stageOne);
+      boolean isNewWave, IdGenerator idGenerator, ProfileManager profiles,
+      Element unsavedIndicatorElement) {
+    super(stageOne, unsavedIndicatorElement);
     Preconditions.checkArgument(stageOne != null);
     Preconditions.checkArgument(waveRef != null);
     Preconditions.checkArgument(waveRef.getWaveId() != null);

Modified: 
incubator/wave/trunk/src/org/waveprotocol/box/webclient/client/StagesProvider.java
URL: 
http://svn.apache.org/viewvc/incubator/wave/trunk/src/org/waveprotocol/box/webclient/client/StagesProvider.java?rev=1332787&r1=1332786&r2=1332787&view=diff
==============================================================================
--- 
incubator/wave/trunk/src/org/waveprotocol/box/webclient/client/StagesProvider.java
 (original)
+++ 
incubator/wave/trunk/src/org/waveprotocol/box/webclient/client/StagesProvider.java
 Tue May  1 19:14:21 2012
@@ -57,6 +57,7 @@ public class StagesProvider extends Stag
   };
 
   private final Element wavePanelElement;
+  private final Element unsavedIndicatorElement;
   private final LogicalPanel rootPanel;
   private final WaveRef waveRef;
   private final RemoteViewServiceMultiplexer channel;
@@ -82,10 +83,12 @@ public class StagesProvider extends Stag
    * @param isNewWave true if the wave is a new client-created wave
    * @param idGenerator
    */
-  public StagesProvider(Element wavePanelElement, LogicalPanel rootPanel, 
WaveRef waveRef,
-      RemoteViewServiceMultiplexer channel, IdGenerator idGenerator, 
ProfileManager profiles,
-      WaveStore store, boolean isNewWave, String localDomain) {
+  public StagesProvider(Element wavePanelElement, Element 
unsavedIndicatorElement,
+      LogicalPanel rootPanel, WaveRef waveRef, RemoteViewServiceMultiplexer 
channel,
+      IdGenerator idGenerator, ProfileManager profiles, WaveStore store, 
boolean isNewWave,
+      String localDomain) {
     this.wavePanelElement = wavePanelElement;
+    this.unsavedIndicatorElement = unsavedIndicatorElement;
     this.rootPanel = rootPanel;
     this.waveRef = waveRef;
     this.channel = channel;
@@ -119,7 +122,7 @@ public class StagesProvider extends Stag
   @Override
   protected AsyncHolder<StageTwo> createStageTwoLoader(StageOne one) {
     return haltIfClosed(new StageTwoProvider(
-        this.one = one, waveRef, channel, isNewWave, idGenerator, profiles));
+        this.one = one, waveRef, channel, isNewWave, idGenerator, profiles, 
unsavedIndicatorElement));
   }
 
   @Override

Modified: 
incubator/wave/trunk/src/org/waveprotocol/box/webclient/client/WebClient.java
URL: 
http://svn.apache.org/viewvc/incubator/wave/trunk/src/org/waveprotocol/box/webclient/client/WebClient.java?rev=1332787&r1=1332786&r2=1332787&view=diff
==============================================================================
--- 
incubator/wave/trunk/src/org/waveprotocol/box/webclient/client/WebClient.java 
(original)
+++ 
incubator/wave/trunk/src/org/waveprotocol/box/webclient/client/WebClient.java 
Tue May  1 19:14:21 2012
@@ -34,6 +34,7 @@ import com.google.gwt.user.client.ui.Roo
 import com.google.gwt.user.client.ui.SplitLayoutPanel;
 import com.google.gwt.user.client.ui.UIObject;
 
+import org.waveprotocol.box.webclient.client.events.Log;
 import org.waveprotocol.box.webclient.client.events.NetworkStatusEvent;
 import org.waveprotocol.box.webclient.client.events.NetworkStatusEventHandler;
 import org.waveprotocol.box.webclient.client.events.WaveCreationEvent;
@@ -48,7 +49,6 @@ import org.waveprotocol.box.webclient.se
 import org.waveprotocol.box.webclient.search.SearchPresenter;
 import org.waveprotocol.box.webclient.search.SimpleSearch;
 import org.waveprotocol.box.webclient.search.WaveStore;
-import org.waveprotocol.box.webclient.client.events.Log;
 import org.waveprotocol.box.webclient.widget.error.ErrorIndicatorPresenter;
 import org.waveprotocol.box.webclient.widget.frame.FramedPanel;
 import org.waveprotocol.box.webclient.widget.loading.LoadingIndicator;
@@ -301,9 +301,10 @@ public class WebClient implements EntryP
     UIObject.setVisible(waveFrame.getElement(), true);
     waveHolder.getElement().appendChild(loading);
     Element holder = 
waveHolder.getElement().appendChild(Document.get().createDivElement());
-    StagesProvider wave = new StagesProvider(
-        holder, waveHolder, waveRef, channel, idGenerator, profiles, 
waveStore, isNewWave,
-        Session.get().getDomain());
+    Element unsavedIndicator = 
Document.get().getElementById("unsavedStateContainer");
+    StagesProvider wave =
+        new StagesProvider(holder, unsavedIndicator, waveHolder, waveRef, 
channel, idGenerator,
+            profiles, waveStore, isNewWave, Session.get().getDomain());
     this.wave = wave;
     wave.load(new Command() {
       @Override

Modified: incubator/wave/trunk/src/org/waveprotocol/wave/client/StageTwo.java
URL: 
http://svn.apache.org/viewvc/incubator/wave/trunk/src/org/waveprotocol/wave/client/StageTwo.java?rev=1332787&r1=1332786&r2=1332787&view=diff
==============================================================================
--- incubator/wave/trunk/src/org/waveprotocol/wave/client/StageTwo.java 
(original)
+++ incubator/wave/trunk/src/org/waveprotocol/wave/client/StageTwo.java Tue May 
 1 19:14:21 2012
@@ -19,6 +19,7 @@ import com.google.common.base.Preconditi
 import com.google.gwt.dom.client.Element;
 import com.google.gwt.user.client.Command;
 
+import org.waveprotocol.box.webclient.client.SavedStateIndicator;
 import org.waveprotocol.wave.client.account.ProfileManager;
 import org.waveprotocol.wave.client.account.impl.ProfileManagerImpl;
 import org.waveprotocol.wave.client.common.util.AsyncHolder;
@@ -85,6 +86,7 @@ import org.waveprotocol.wave.concurrency
 import org.waveprotocol.wave.concurrencycontrol.channel.ViewChannelFactory;
 import org.waveprotocol.wave.concurrencycontrol.channel.ViewChannelImpl;
 import org.waveprotocol.wave.concurrencycontrol.channel.WaveViewService;
+import org.waveprotocol.wave.concurrencycontrol.common.UnsavedDataListener;
 import 
org.waveprotocol.wave.concurrencycontrol.common.UnsavedDataListenerFactory;
 import org.waveprotocol.wave.model.conversation.ConversationBlip;
 import org.waveprotocol.wave.model.conversation.ConversationThread;
@@ -237,8 +239,11 @@ public interface StageTwo {
     private DiffController diffController;
     private Reader reader;
 
-    public DefaultProvider(StageOne stageOne) {
+    private final Element unsavedIndicatorElement;
+
+    public DefaultProvider(StageOne stageOne, Element unsavedIndicatorElement) 
{
       this.stageOne = stageOne;
+      this.unsavedIndicatorElement = unsavedIndicatorElement;
     }
 
     /**
@@ -525,7 +530,20 @@ public interface StageTwo {
           .build();
 
       ViewChannelFactory viewFactory = 
ViewChannelImpl.factory(createWaveViewService(), logger);
-      UnsavedDataListenerFactory unsyncedListeners = 
UnsavedDataListenerFactory.NONE;
+      UnsavedDataListenerFactory unsyncedListeners = new 
UnsavedDataListenerFactory() {
+
+        private final UnsavedDataListener listener = new SavedStateIndicator(
+            unsavedIndicatorElement);
+
+        @Override
+        public UnsavedDataListener create(WaveletId waveletId) {
+          return listener;
+        }
+
+        @Override
+        public void destroy(WaveletId waveletId) {
+        }
+      };
 
       WaveletId udwId = 
getIdGenerator().newUserDataWaveletId(getSignedInUser().getAddress());
       final IdFilter filter = IdFilter.of(Collections.singleton(udwId),


Reply via email to