Author: yurize
Date: Fri Nov 18 11:56:28 2011
New Revision: 1203614
URL: http://svn.apache.org/viewvc?rev=1203614&view=rev
Log:
Adds auto focusing on the oldest unread/newest modified blip when opening a
wave. http://codereview.waveprotocol.org/610001/show
Added:
incubator/wave/trunk/src/org/waveprotocol/wave/client/wavepanel/impl/focus/FocusBlipSelector.java
(with props)
Modified:
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/wave/client/StageTwo.java
incubator/wave/trunk/src/org/waveprotocol/wave/client/wavepanel/impl/reader/Reader.java
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=1203614&r1=1203613&r2=1203614&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
Fri Nov 18 11:56:28 2011
@@ -25,14 +25,20 @@ import org.waveprotocol.wave.client.Stag
import org.waveprotocol.wave.client.StageTwo;
import org.waveprotocol.wave.client.account.ProfileManager;
import org.waveprotocol.wave.client.common.util.AsyncHolder;
+import org.waveprotocol.wave.client.wavepanel.impl.focus.FocusBlipSelector;
+import org.waveprotocol.wave.client.wavepanel.impl.focus.ViewTraverser;
+import org.waveprotocol.wave.client.wavepanel.impl.reader.Reader;
+import org.waveprotocol.wave.client.wavepanel.view.BlipView;
+import org.waveprotocol.wave.client.wavepanel.view.dom.ModelAsViewProvider;
import org.waveprotocol.wave.concurrencycontrol.channel.WaveViewService;
+import org.waveprotocol.wave.model.conversation.ConversationView;
import org.waveprotocol.wave.model.id.IdGenerator;
-import org.waveprotocol.wave.model.id.WaveId;
import org.waveprotocol.wave.model.schema.SchemaProvider;
import org.waveprotocol.wave.model.schema.conversation.ConversationSchemas;
import org.waveprotocol.wave.model.wave.ParticipantId;
import org.waveprotocol.wave.model.wave.data.WaveViewData;
import org.waveprotocol.wave.model.wave.data.impl.WaveViewDataImpl;
+import org.waveprotocol.wave.model.waveref.WaveRef;
/**
* Provides stage 2 of the staged loading of the wave panel
@@ -41,7 +47,7 @@ import org.waveprotocol.wave.model.wave.
*/
public class StageTwoProvider extends StageTwo.DefaultProvider {
- private final WaveId waveId;
+ private final WaveRef waveRef;
private final RemoteViewServiceMultiplexer channel;
private final boolean isNewWave;
// TODO: Remove this after WebClientBackend is deleted.
@@ -61,12 +67,13 @@ public class StageTwoProvider extends St
* @param channel communication channel
* @param idGenerator
*/
- public StageTwoProvider(StageOne stageOne, WaveId waveId,
RemoteViewServiceMultiplexer channel,
+ public StageTwoProvider(StageOne stageOne, WaveRef waveRef,
RemoteViewServiceMultiplexer channel,
boolean isNewWave, IdGenerator idGenerator, ProfileManager profiles) {
super(stageOne);
Preconditions.checkArgument(stageOne != null);
- Preconditions.checkArgument(waveId != null);
- this.waveId = waveId;
+ Preconditions.checkArgument(waveRef != null);
+ Preconditions.checkArgument(waveRef.getWaveId() != null);
+ this.waveRef = waveRef;
this.channel = channel;
this.isNewWave = isNewWave;
this.idGenerator = idGenerator;
@@ -95,7 +102,7 @@ public class StageTwoProvider extends St
@Override
protected WaveViewService createWaveViewService() {
- return new RemoteWaveViewService(waveId, channel, getDocumentRegistry());
+ return new RemoteWaveViewService(waveRef.getWaveId(), channel,
getDocumentRegistry());
}
/**
@@ -129,9 +136,12 @@ public class StageTwoProvider extends St
// Install eager UI.
installFeatures();
+ Reader reader = installReader();
// Rendering, and therefore the whole stage is now ready.
whenReady.use(StageTwoProvider.this);
+
+ selectAndFocusOnBlip(reader);
}
});
}
@@ -155,6 +165,22 @@ public class StageTwoProvider extends St
@Override
protected void fetchWave(final AsyncHolder.Accessor<WaveViewData> whenReady)
{
- whenReady.use(WaveViewDataImpl.create(waveId));
+ whenReady.use(WaveViewDataImpl.create(waveRef.getWaveId()));
+ }
+
+ /**
+ * Finds the blip that should receive the focus and selects it.
+ */
+ private void selectAndFocusOnBlip(Reader reader) {
+ ModelAsViewProvider views = getModelAsViewProvider();
+ ConversationView wave = getConversations();
+ StageOne one = getStageOne();
+ FocusBlipSelector blipSelector =
+ FocusBlipSelector.create(waveRef, wave, views, reader, new
ViewTraverser());
+ BlipView blipUi = blipSelector.select();
+ // Focus on the selected blip.
+ if (blipUi != null) {
+ one.getFocusFrame().focus(blipUi);
+ }
}
}
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=1203614&r1=1203613&r2=1203614&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
Fri Nov 18 11:56:28 2011
@@ -34,11 +34,8 @@ import org.waveprotocol.wave.client.comm
import org.waveprotocol.wave.client.wavepanel.view.BlipView;
import org.waveprotocol.wave.client.wavepanel.view.dom.ModelAsViewProvider;
import org.waveprotocol.wave.client.wavepanel.view.dom.full.BlipQueueRenderer;
-import org.waveprotocol.wave.model.conversation.Conversation;
-import org.waveprotocol.wave.model.conversation.ConversationBlip;
import org.waveprotocol.wave.model.conversation.ConversationView;
import org.waveprotocol.wave.model.id.IdGenerator;
-import org.waveprotocol.wave.model.id.ModernIdSerialiser;
import org.waveprotocol.wave.model.waveref.WaveRef;
/**
@@ -118,7 +115,7 @@ public class StagesProvider extends Stag
@Override
protected AsyncHolder<StageTwo> createStageTwoLoader(StageOne one) {
return haltIfClosed(new StageTwoProvider(
- this.one = one, waveRef.getWaveId(), channel, isNewWave, idGenerator,
profiles));
+ this.one = one, waveRef, channel, isNewWave, idGenerator, profiles));
}
@Override
@@ -172,33 +169,8 @@ public class StagesProvider extends Stag
}
private void handleExistingWave(StageThree three) {
- // If there's blip reference then focus on that blip.
- String documentId = waveRef.getDocumentId();
- if (documentId != null) {
- ModelAsViewProvider views = two.getModelAsViewProvider();
- BlipQueueRenderer blipQueue = two.getBlipQueue();
- ConversationView wave = two.getConversations();
- blipQueue.flush();
- // Find conversation
- Conversation conversation;
- if (waveRef.hasWaveletId()) {
- String id =
ModernIdSerialiser.INSTANCE.serialiseWaveletId(waveRef.getWaveletId());
- conversation = wave.getConversation(id);
- } else {
- // Unspecified wavelet means root.
- conversation = wave.getRoot();
- }
- if (conversation != null) {
- // Find selected blip.
- ConversationBlip blip = wave.getRoot().getBlip(documentId);
- if (blip != null) {
- BlipView blipUi = views.getBlipView(blip);
- if (blipUi != null) {
- two.getStageOne().getFocusFrame().focus(blipUi);
- }
- }
- }
- }
+ BlipQueueRenderer blipQueue = two.getBlipQueue();
+ blipQueue.flush();
}
public void destroy() {
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=1203614&r1=1203613&r2=1203614&view=diff
==============================================================================
--- incubator/wave/trunk/src/org/waveprotocol/wave/client/StageTwo.java
(original)
+++ incubator/wave/trunk/src/org/waveprotocol/wave/client/StageTwo.java Fri Nov
18 11:56:28 2011
@@ -690,6 +690,7 @@ public interface StageTwo {
// Install eager UI features
installFeatures();
+ installReader();
// Activate liveness.
getConnector().connect(null);
@@ -711,8 +712,13 @@ public interface StageTwo {
*/
protected void installFeatures() {
// Eagerly install some features.
- Reader.install(getSupplement(), stageOne.getFocusFrame(),
getModelAsViewProvider(),
+ }
+
+ protected Reader installReader() {
+ Reader reader =
+ Reader.install(getSupplement(), stageOne.getFocusFrame(),
getModelAsViewProvider(),
getDocumentRegistry());
+ return reader;
}
}
}
Added:
incubator/wave/trunk/src/org/waveprotocol/wave/client/wavepanel/impl/focus/FocusBlipSelector.java
URL:
http://svn.apache.org/viewvc/incubator/wave/trunk/src/org/waveprotocol/wave/client/wavepanel/impl/focus/FocusBlipSelector.java?rev=1203614&view=auto
==============================================================================
---
incubator/wave/trunk/src/org/waveprotocol/wave/client/wavepanel/impl/focus/FocusBlipSelector.java
(added)
+++
incubator/wave/trunk/src/org/waveprotocol/wave/client/wavepanel/impl/focus/FocusBlipSelector.java
Fri Nov 18 11:56:28 2011
@@ -0,0 +1,153 @@
+/**
+ * Copyright 2011 Google Inc.
+ *
+ * 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.wave.client.wavepanel.impl.focus;
+
+import
org.waveprotocol.wave.client.wavepanel.impl.focus.FocusFramePresenter.FocusOrder;
+import org.waveprotocol.wave.client.wavepanel.impl.reader.Reader;
+import org.waveprotocol.wave.client.wavepanel.view.BlipView;
+import org.waveprotocol.wave.client.wavepanel.view.dom.ModelAsViewProvider;
+import org.waveprotocol.wave.model.conversation.Conversation;
+import org.waveprotocol.wave.model.conversation.ConversationBlip;
+import org.waveprotocol.wave.model.conversation.ConversationView;
+import org.waveprotocol.wave.model.id.ModernIdSerialiser;
+import org.waveprotocol.wave.model.util.CollectionUtils;
+import org.waveprotocol.wave.model.waveref.WaveRef;
+
+import java.util.Collections;
+import java.util.Map;
+
+/**
+ * Selects the blip that should should receive the focus.
+ *
+ * @author [email protected] (Yuri Zelikov)
+ */
+public class FocusBlipSelector {
+
+ /** The reference to the current wave. */
+ private final WaveRef waveRef;
+
+ /** The conversation. */
+ private final ConversationView wave;
+
+ /** The model with views. */
+ private final ModelAsViewProvider views;
+
+ /** The blip ordering. */
+ private final ViewTraverser traverser;
+
+ /** The ordering for focus frame movement. */
+ private final Reader reader;
+
+ /**
+ * Creates a {@link FocusBlipSelector}.
+ *
+ * @param waveRef the reference to the current wave that includes the blip id
+ * in case the wave was loaded due to a click on a link to this wave.
+ * @param wave the he conversation.
+ * @param views the model with views.
+ * @param traverser the blip ordering.
+ * @param reader the ordering of focus frame movement.
+ * @return the focus blip selector.
+ */
+ public static FocusBlipSelector create(WaveRef waveRef, ConversationView
wave,
+ ModelAsViewProvider views, Reader reader, ViewTraverser traverser) {
+ return new FocusBlipSelector(waveRef, wave, views, reader, traverser);
+ }
+
+ FocusBlipSelector(WaveRef waveRef, ConversationView wave,
+ ModelAsViewProvider models, Reader reader, ViewTraverser traverser) {
+ this.waveRef = waveRef;
+ this.wave = wave;
+ this.views = models;
+ this.reader = reader;
+ this.traverser = traverser;
+ }
+
+ /**
+ * @return the blip that should receive the focus or {@code null} if it was
+ * impossible to compute it. The strategy for blip selection is like
+ * follows:
+ * <ul>
+ * <li>Try to select the oldest unread blip using {@link
FocusOrder}.</li>
+ * <li>If all blips are already read, then select the most recently
+ * modified blip.</li>
+ * </ul>
+ */
+ public BlipView select() {
+ // Determine if waveRef has a documentId in it - if so, the referenced blip
+ // should receive the focus on wave load.
+ // First find conversation
+ Conversation conversation;
+ if (waveRef.hasWaveletId()) {
+ String id =
ModernIdSerialiser.INSTANCE.serialiseWaveletId(waveRef.getWaveletId());
+ conversation = wave.getConversation(id);
+ } else {
+ // Unspecified wavelet means root.
+ conversation = wave.getRoot();
+ }
+ if (conversation == null) {
+ return null;
+ } else {
+ ConversationBlip blip = null;
+ // If there's blip reference then focus on that blip.
+ String documentId = waveRef.getDocumentId();
+ // Find selected blip.
+ if (documentId != null) {
+ blip = wave.getRoot().getBlip(documentId);
+ if (blip != null) {
+ return views.getBlipView(blip);
+ } else {
+ return null;
+ }
+ } else {
+ blip = wave.getRoot().getRootThread().getFirstBlip();
+ BlipView rootBlipUi = views.getBlipView(blip);
+ if (rootBlipUi == null) {
+ return null;
+ }
+ if (reader != null) {
+ if (!reader.isRead(rootBlipUi)) {
+ // Return the root blip since it is unread.
+ return rootBlipUi;
+ }
+ // If no blip was referenced, then try to find the next blip starting
+ // from the root according to the order.
+ BlipView tempUi = reader.getNext(rootBlipUi);
+ if (tempUi != null && !tempUi.getId().equals(rootBlipUi.getId())) {
+ return tempUi;
+ } else {
+ return findMostRecentlyModified(rootBlipUi);
+ }
+ } else {
+ return findMostRecentlyModified(rootBlipUi);
+ }
+ }
+ }
+ }
+
+ private BlipView findMostRecentlyModified(BlipView start) {
+ BlipView blipUi = start;
+ Map<Long, BlipView> blips = CollectionUtils.newHashMap();
+ while (blipUi != null) {
+ ConversationBlip blip = views.getBlip(blipUi);
+ blips.put(blip.getLastModifiedTime() , blipUi);
+ blipUi = traverser.getNext(blipUi);
+ }
+ long lmt = Collections.max(blips.keySet());
+ return blips.get(lmt);
+ }
+}
Propchange:
incubator/wave/trunk/src/org/waveprotocol/wave/client/wavepanel/impl/focus/FocusBlipSelector.java
------------------------------------------------------------------------------
svn:executable = *
Modified:
incubator/wave/trunk/src/org/waveprotocol/wave/client/wavepanel/impl/reader/Reader.java
URL:
http://svn.apache.org/viewvc/incubator/wave/trunk/src/org/waveprotocol/wave/client/wavepanel/impl/reader/Reader.java?rev=1203614&r1=1203613&r2=1203614&view=diff
==============================================================================
---
incubator/wave/trunk/src/org/waveprotocol/wave/client/wavepanel/impl/reader/Reader.java
(original)
+++
incubator/wave/trunk/src/org/waveprotocol/wave/client/wavepanel/impl/reader/Reader.java
Fri Nov 18 11:56:28 2011
@@ -86,7 +86,7 @@ public final class Reader implements Foc
}
}
- private boolean isRead(BlipView blipUi) {
+ public boolean isRead(BlipView blipUi) {
return !supplement.isUnread(models.getBlip(blipUi));
}