package org.activityinfo.ui.client.widget;
/*
* #%L
* ActivityInfo Server
* %%
* Copyright (C) 2009 - 2013 UNICEF
* %%
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program. If not, see
* <http://www.gnu.org/licenses/gpl-3.0.html>.
* #L%
*/
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.HTML;
import org.activityinfo.promise.Promise;
import org.activityinfo.ui.client.style.ElementStyle;
import org.activityinfo.ui.client.widget.loading.ExceptionOracle;
/**
* @author yuriyz on 4/1/14.
*/
public class ConfirmDialog {
/**
* Maintain a single instance of this dialog, as there is by definition never more than one
* modal dialog shown at a time.
*/
private static ConfirmDialog INSTANCE = null;
/**
* @author yuriyz on 4/8/14.
*/
public static class Messages {
private String titleText;
private String messageText;
private String primaryButtonText;
public Messages(String titleText, String messageText, String primaryButtonText) {
this.titleText = titleText;
this.messageText = messageText;
this.primaryButtonText = primaryButtonText;
}
public String getTitleText() {
return titleText;
}
public String getMessageText() {
return messageText;
}
public String getPrimaryButtonText() {
return primaryButtonText;
}
}
/**
* @author yuriyz on 4/7/14.
*/
public static interface Action {
Messages getConfirmationMessages();
Messages getProgressMessages();
Messages getFailureMessages();
ElementStyle getPrimaryButtonStyle();
/**
*
* Invoked when the user has confirmed the action, or is retrying.
*/
Promise<Void> execute();
/**
* Invoked when the action completes successfully
*/
void onComplete();
}
public static enum State {
CONFIRM, PROGRESS, FAILED
}
private final ModalDialog dialog;
private final HTML failedMessageContainer = new HTML();
private final HTML messageContainer = new HTML();
private State state;
private Action action;
private ConfirmDialog() {
dialog = new ModalDialog();
dialog.getModalBody().add(failedMessageContainer);
dialog.getModalBody().add(messageContainer);
dialog.getPrimaryButton().addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
tryAction();
}
});
}
/**
* Shows the confirmation dialog
*/
public static void confirm(Action action) {
if(INSTANCE == null) {
INSTANCE = new ConfirmDialog();
}
INSTANCE.action = action;
INSTANCE.dialog.getPrimaryButton().setStyleName("btn btn-" + action.getPrimaryButtonStyle().name().toLowerCase());
INSTANCE.updateState(State.CONFIRM, null);
INSTANCE.dialog.show();
}
private void tryAction() {
updateState(State.PROGRESS, null);
action.execute().then(new AsyncCallback<Void>() {
@Override
public void onFailure(Throwable caught) {
showFailureDelayed(caught);
}
@Override
public void onSuccess(Void result) {
ConfirmDialog.this.dialog.hide();
action.onComplete();
}
});
}
private void showFailureDelayed(final Throwable caught) {
// Show failure message only after a short fixed delay to ensure that
// the progress stage is displayed. Otherwise if we have a synchronous error, clicking
// the retry button will look like it's not working.
Scheduler.get().scheduleFixedDelay(new Scheduler.RepeatingCommand() {
@Override
public boolean execute() {
updateState(State.FAILED, caught);
return false;
}
}, 500);
}
private void updateState(State state, Throwable caught) {
this.state = state;
// Disable both ok and cancel button while we're waiting for the
// asynchronous action to complete. We have no reliable way of cancelling
// a delete action that has been sent to the server for example.
dialog.getPrimaryButton().setEnabled(state != State.PROGRESS);
dialog.getCancelButton().setEnabled(state != State.PROGRESS);
failedMessageContainer.setVisible(state == State.FAILED);
switch (state) {
case CONFIRM:
updateMessages(action.getConfirmationMessages());
break;
case FAILED:
updateMessages(action.getFailureMessages());
failedMessageContainer.setHTML(Templates.WARNING_MESSAGE_TEMPLATE.html(ExceptionOracle.getExplanation(caught)));
break;
case PROGRESS:
updateMessages(action.getProgressMessages());
break;
}
}
private void updateMessages(Messages messages) {
dialog.setDialogTitle(messages.getTitleText());
messageContainer.setHTML(messages.getMessageText());
if(state == State.PROGRESS) {
dialog.getPrimaryButton().setHTML(Templates.OK_BTN_TEMPLATE.html(messages.getPrimaryButtonText()));
} else {
dialog.getPrimaryButton().setText(messages.getPrimaryButtonText());
}
}
}