package org.activityinfo.ui.client.component.importDialog; import com.google.common.base.Function; import com.google.common.collect.Lists; import com.google.gwt.core.client.Scheduler; import com.google.gwt.core.shared.GWT; import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.ClickHandler; import com.google.gwt.event.shared.EventBus; import com.google.gwt.event.shared.SimpleEventBus; import com.google.gwt.user.client.rpc.AsyncCallback; import com.google.gwt.user.client.ui.HTML; import org.activityinfo.core.client.ResourceLocator; import org.activityinfo.core.client.form.tree.AsyncFormTreeBuilder; import org.activityinfo.model.resource.ResourceId; import org.activityinfo.model.formTree.FormTree; import org.activityinfo.core.shared.importing.model.ImportModel; import org.activityinfo.core.shared.importing.model.MapExistingAction; import org.activityinfo.core.shared.importing.strategy.FieldImportStrategies; import org.activityinfo.core.shared.importing.strategy.ImportTarget; import org.activityinfo.promise.Promise; import org.activityinfo.i18n.shared.I18N; import org.activityinfo.ui.client.component.importDialog.mapping.ColumnMappingPage; import org.activityinfo.ui.client.component.importDialog.source.ChooseSourcePage; import org.activityinfo.ui.client.component.importDialog.validation.ValidationPage; import org.activityinfo.ui.client.widget.FullScreenOverlay; import org.activityinfo.ui.client.widget.ModalDialog; import java.util.List; import java.util.ListIterator; public class ImportPresenter { private final EventBus eventBus = GWT.create(SimpleEventBus.class); private final ImportModel importModel; private final Importer importer; private ImportDialog dialogBox = new ImportDialog(); private FullScreenOverlay overlay = new FullScreenOverlay(); private List<ImportPage> pages; private ImportPage currentPage; public ImportPresenter(ResourceLocator resourceLocator, FormTree formTree) { this.importModel = new ImportModel(formTree); this.importer = new Importer(resourceLocator, formTree, FieldImportStrategies.get()); final ChooseSourcePage chooseSourcePage = new ChooseSourcePage(importModel, eventBus); final ColumnMappingPage matchingPage = new ColumnMappingPage(importModel, createMatchingColumnActions(), eventBus); final ValidationPage validationPage = new ValidationPage(importModel, importer); pages = Lists.<ImportPage>newArrayList(chooseSourcePage, matchingPage, validationPage); dialogBox.getPreviousButton().addClickHandler(new ClickHandler() { @Override public void onClick(ClickEvent event) { previousPage(); } }); dialogBox.getNextButton().addClickHandler(new ClickHandler() { @Override public void onClick(ClickEvent event) { nextPage(); } }); dialogBox.getCancelButton().addClickHandler(new ClickHandler() { @Override public void onClick(ClickEvent event) { overlay.hide(); } }); dialogBox.getFinishButton().addClickHandler(new ClickHandler() { @Override public void onClick(ClickEvent event) { int invalidRowsCount = validationPage.getInvalidRowsCount(); if (invalidRowsCount > 0) { final ModalDialog confirmDialog = new ModalDialog(); confirmDialog.getModalBody().add(new HTML(I18N.MESSAGES.continueImportWithInvalidRows(invalidRowsCount))); confirmDialog.getPrimaryButton().addClickHandler(new ClickHandler() { @Override public void onClick(ClickEvent event) { confirmDialog.hide(); persistData(); } }); confirmDialog.show(); } else { // all rows are valid -> persist directly persistData(); } } }); dialogBox.getTitleWidget().setText(I18N.CONSTANTS.importDialogTitle()); eventBus.addHandler(PageChangedEvent.TYPE, new PageChangedEventHandler() { @Override public void onPageChanged(PageChangedEvent event) { if (event.isValid()) { dialogBox.getStatusText().removeClassName("alert"); } else { dialogBox.getStatusText().addClassName("alert"); } dialogBox.setStatusText(event.getStatusMessage()); } }); setButtonsState(); } private List<MapExistingAction> createMatchingColumnActions() { final List<MapExistingAction> columnActions = Lists.newArrayList(); final List<ImportTarget> importTargets = importer.getImportTargets(); for (ImportTarget target : importTargets) { columnActions.add(new MapExistingAction(target)); } return columnActions; } protected void persistData() { dialogBox.getFinishButton().setEnabled(false); dialogBox.setStatusText(I18N.CONSTANTS.importing()); importer.persist(importModel).then(new AsyncCallback<Void>() { @Override public void onFailure(Throwable caught) { showDelayedFailure(caught); } @Override public void onSuccess(Void result) { overlay.hide(); Scheduler.get().scheduleDeferred(new Scheduler.ScheduledCommand() { @Override public void execute() { eventBus.fireEvent(new ImportResultEvent(true)); } }); } }); } private void showDelayedFailure(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() { dialogBox.setStatusText(I18N.CONSTANTS.importFailed()); dialogBox.getFinishButton().setText(I18N.CONSTANTS.retry()); dialogBox.getFinishButton().setEnabled(true); eventBus.fireEvent(new ImportResultEvent(false)); return false; } }, 500); } public void show() { gotoPage(pages.get(0)); overlay.show(dialogBox); } private void gotoPage(ImportPage page) { currentPage = page; currentPage.start(); dialogBox.setPage(currentPage); setButtonsState(); } private void nextPage() { int nextIndex = pages.indexOf(currentPage) + 1; if (nextIndex < pages.size()) { if (currentPage.isValid()) { gotoPage(pages.get(nextIndex)); } else { currentPage.fireStateChanged(); } } } private void previousPage() { int prevIndex = pages.indexOf(currentPage) - 1; if (prevIndex >= 0) { gotoPage(pages.get(prevIndex)); } dialogBox.setStatusText(""); // clear status text } private void setButtonsState() { int index = pages.indexOf(currentPage); dialogBox.getPreviousButton().setEnabled((index-1) >= 0); dialogBox.getNextButton().setEnabled((index+1) < pages.size()); dialogBox.getFinishButton().setVisible((index+1) == pages.size() && currentPage.isValid()); } public EventBus getEventBus() { return eventBus; } public static Promise<ImportPresenter> showPresenter(ResourceId activityId, final ResourceLocator resourceLocator) { AsyncFormTreeBuilder treeBuilder = new AsyncFormTreeBuilder(resourceLocator); return treeBuilder.apply(activityId).then(new Function<FormTree, ImportPresenter>() { @Override public ImportPresenter apply(FormTree input) { return new ImportPresenter(resourceLocator, input); } }); } }