blob: a2c8ada15d19a552514226521e3be788541cf89e [file] [log] [blame]
/*
* 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 com.google.gwt.sample.expenses.client.place;
import com.google.gwt.activity.shared.Activity;
import com.google.gwt.event.shared.EventBus;
import com.google.gwt.place.shared.PlaceController;
import com.google.gwt.requestfactory.client.RequestFactoryEditorDriver;
import com.google.gwt.requestfactory.shared.EntityProxy;
import com.google.gwt.requestfactory.shared.EntityProxyId;
import com.google.gwt.requestfactory.shared.Receiver;
import com.google.gwt.requestfactory.shared.RequestContext;
import com.google.gwt.requestfactory.shared.ServerFailure;
import com.google.gwt.requestfactory.shared.Violation;
import com.google.gwt.sample.expenses.client.place.ProxyPlace.Operation;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.AcceptsOneWidget;
import java.util.Set;
/**
* Abstract activity for editing a record. Subclasses must provide access to the
* request that will be fired when Save is clicked.
* <p>
* Instances are not reusable. Once an activity is stoped, it cannot be
* restarted.
*
* @param <P> the type of Proxy being edited
*/
public abstract class AbstractProxyEditActivity<P extends EntityProxy> implements Activity,
ProxyEditView.Delegate {
private final ProxyEditView<P, ?> view;
private final PlaceController placeController;
private RequestFactoryEditorDriver<P, ?> editorDriver;
private boolean waiting;
public AbstractProxyEditActivity(ProxyEditView<P, ?> view, PlaceController placeController) {
this.view = view;
this.placeController = placeController;
}
public void cancelClicked() {
String unsavedChangesWarning = mayStop();
if ((unsavedChangesWarning == null)
|| Window.confirm(unsavedChangesWarning)) {
editorDriver = null;
exit(false);
}
}
public String mayStop() {
if (isWaiting() || changed()) {
return "Are you sure you want to abandon your changes?";
}
return null;
}
public void onCancel() {
onStop();
}
public void onStop() {
view.setDelegate(null);
editorDriver = null;
}
public void saveClicked() {
if (!changed()) {
return;
}
setWaiting(true);
editorDriver.flush().fire(new Receiver<Void>() {
/*
* Callbacks do nothing if editorDriver is null, we were stopped in
* midflight
*/
@Override
public void onFailure(ServerFailure error) {
if (editorDriver != null) {
setWaiting(false);
super.onFailure(error);
}
}
@Override
public void onSuccess(Void ignore) {
if (editorDriver != null) {
// We want no warnings from mayStop, so:
// Defeat isChanged check
editorDriver = null;
// Defeat call-in-flight check
setWaiting(false);
exit(true);
}
}
@Override
public void onViolation(Set<Violation> errors) {
if (editorDriver != null) {
setWaiting(false);
editorDriver.setViolations(errors);
}
}
});
}
public void start(AcceptsOneWidget display, EventBus eventBus) {
editorDriver = view.createEditorDriver();
view.setDelegate(this);
editorDriver.edit(getProxy(), createSaveRequest(getProxy()));
display.setWidget(view);
}
/**
* Called once to create the appropriate request to save
* changes.
*
* @return the request context to fire when the save button is clicked
*/
protected abstract RequestContext createSaveRequest(P proxy);
/**
* Called when the user cancels or has successfully saved. This default
* implementation tells the {@link PlaceController} to show the details of the
* edited record.
*
* @param saved true if changes were comitted, false if user canceled
*/
protected void exit(@SuppressWarnings("unused") boolean saved) {
placeController.goTo(new ProxyPlace(getProxyId(), Operation.DETAILS));
}
/**
* Get the proxy to be edited. Must be mutable, typically via a call to
* {@link RequestContext#edit(EntityProxy)}, or
* {@link RequestContext#create(Class)}.
*/
protected abstract P getProxy();
@SuppressWarnings("unchecked")
// id type always matches proxy type
protected EntityProxyId<P> getProxyId() {
return (EntityProxyId<P>) getProxy().stableId();
}
private boolean changed() {
return editorDriver != null && editorDriver.flush().isChanged();
}
/**
* @return true if we're waiting for an rpc response.
*/
private boolean isWaiting() {
return waiting;
}
/**
* While we are waiting for a response, we cannot poke setters on the proxy
* (that is, we cannot call editorDriver.flush). So we set the waiting flag to
* warn ourselves not to, and to disable the view.
*/
private void setWaiting(boolean wait) {
this.waiting = wait;
view.setEnabled(!wait);
}
}