/**
* Copyright 2010 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.box.webclient.client;
import com.google.gwt.dom.client.Element;
import org.waveprotocol.box.webclient.search.WaveContext;
import org.waveprotocol.box.webclient.search.WaveStore;
import org.waveprotocol.wave.client.StageOne;
import org.waveprotocol.wave.client.StageThree;
import org.waveprotocol.wave.client.StageTwo;
import org.waveprotocol.wave.client.StageZero;
import org.waveprotocol.wave.client.Stages;
import org.waveprotocol.wave.client.account.ProfileManager;
import org.waveprotocol.wave.client.common.util.AsyncHolder;
import org.waveprotocol.wave.client.common.util.AsyncHolder.Accessor;
import org.waveprotocol.wave.client.common.util.LogicalPanel;
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;
/**
* Stages for loading the undercurrent Wave Panel
*
* @author zdwang@google.com (David Wang)
*/
public class StagesProvider extends Stages {
private final static AsyncHolder<Object> HALT = new AsyncHolder<Object>() {
@Override
public void call(Accessor<Object> accessor) {
// Never ready, so never notify the accessor.
}
};
private final Element wavePanelElement;
private final LogicalPanel rootPanel;
private final WaveRef waveRef;
private final RemoteViewServiceMultiplexer channel;
private final IdGenerator idGenerator;
private final ProfileManager profiles;
private final WaveStore waveStore;
private final boolean isNewWave;
private final String localDomain;
private boolean closed;
private StageOne one;
private StageTwo two;
private StageThree three;
private WaveContext wave;
/**
* @param wavePanelElement The dom element to become the wave panel
* @param rootPanel A panel that this an ancestor of wavePanelElement. This is
* used for adopting to the GWT widget tree.
* @param waveRef the id of the wave to open. If null, it means, create a new
* wave.
* @param channel communication channel.
* @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) {
this.wavePanelElement = wavePanelElement;
this.rootPanel = rootPanel;
this.waveRef = waveRef;
this.channel = channel;
this.idGenerator = idGenerator;
this.profiles = profiles;
this.waveStore = store;
this.isNewWave = isNewWave;
this.localDomain = localDomain;
}
@Override
protected AsyncHolder<StageZero> createStageZeroLoader() {
return haltIfClosed(super.createStageZeroLoader());
}
@Override
protected AsyncHolder<StageOne> createStageOneLoader(StageZero zero) {
return haltIfClosed(new StageOne.DefaultProvider(zero) {
@Override
protected Element createWaveHolder() {
return wavePanelElement;
}
@Override
protected LogicalPanel createWaveContainer() {
return rootPanel;
}
});
}
@Override
protected AsyncHolder<StageTwo> createStageTwoLoader(StageOne one) {
return haltIfClosed(new StageTwoProvider(
this.one = one, waveRef.getWaveId(), channel, isNewWave, idGenerator, profiles));
}
@Override
protected AsyncHolder<StageThree> createStageThreeLoader(final StageTwo two) {
return haltIfClosed(new StageThree.DefaultProvider(this.two = two) {
@Override
protected void create(final Accessor<StageThree> whenReady) {
// Prepend an init wave flow onto the stage continuation.
super.create(new Accessor<StageThree>() {
@Override
public void use(StageThree x) {
onStageThreeLoaded(x, whenReady);
}
});
}
@Override
protected String getLocalDomain() {
return localDomain;
}
});
}
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);
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();
// Force rendering to finish.
blipQueue.flush();
BlipView blipUi = views.getBlipView(wave.getRoot().getRootThread().getFirstBlip());
three.getEditActions().startEditing(blipUi);
}
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);
}
}
}
}
}
public void destroy() {
if (wave != null) {
waveStore.remove(wave);
wave = null;
}
if (three != null) {
three.getEditActions().stopEditing();
three = null;
}
if (two != null) {
two.getConnector().close();
two = null;
}
if (one != null) {
one.getWavePanel().destroy();
one = null;
}
closed = true;
}
/**
* @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) {
return closed ? (AsyncHolder<T>) HALT : provider;
}
}