1 /*
2  * Copyright 2010 Google Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5  * use this file except in compliance with the License. You may obtain a copy of
6  * the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13  * License for the specific language governing permissions and limitations under
14  * the License.
15  */
16 package com.google.gwt.sample.dynatablerf.client;
17 
18 import com.google.gwt.core.client.GWT;
19 import com.google.gwt.event.dom.client.ClickEvent;
20 import com.google.gwt.event.dom.client.KeyCodes;
21 import com.google.gwt.event.dom.client.KeyUpEvent;
22 import com.google.gwt.event.dom.client.KeyUpHandler;
23 import com.google.gwt.event.logical.shared.ValueChangeEvent;
24 import com.google.gwt.event.shared.EventBus;
25 import com.google.gwt.sample.dynatablerf.client.events.EditPersonEvent;
26 import com.google.gwt.sample.dynatablerf.client.widgets.MentorSelector;
27 import com.google.gwt.sample.dynatablerf.client.widgets.PersonEditor;
28 import com.google.gwt.sample.dynatablerf.client.widgets.ScheduleEditor;
29 import com.google.gwt.sample.dynatablerf.client.widgets.TimeSlotListWidget;
30 import com.google.gwt.sample.dynatablerf.shared.DynaTableRequestFactory;
31 import com.google.gwt.sample.dynatablerf.shared.DynaTableRequestFactory.PersonRequest;
32 import com.google.gwt.sample.dynatablerf.shared.PersonProxy;
33 import com.google.gwt.uibinder.client.UiBinder;
34 import com.google.gwt.uibinder.client.UiField;
35 import com.google.gwt.uibinder.client.UiHandler;
36 import com.google.gwt.user.client.ui.CheckBox;
37 import com.google.gwt.user.client.ui.DialogBox;
38 import com.google.gwt.user.client.ui.HTMLPanel;
39 import com.google.web.bindery.requestfactory.gwt.client.RequestFactoryEditorDriver;
40 import com.google.web.bindery.requestfactory.shared.Receiver;
41 import com.google.web.bindery.requestfactory.shared.Request;
42 import com.google.web.bindery.requestfactory.shared.RequestContext;
43 
44 import java.util.Set;
45 
46 import javax.validation.ConstraintViolation;
47 
48 /**
49  * This class shows how the UI for editing a person is wired up to the
50  * RequestFactoryEditorDelegate. It is also responsible for showing and
51  * dismissing the PersonEditor. The use of the FavoriteManager shows integration
52  * between a remote service and a local service.
53  */
54 public class PersonEditorWorkflow {
55   interface Binder extends UiBinder<DialogBox, PersonEditorWorkflow> {
56     Binder BINDER = GWT.create(Binder.class);
57   }
58 
59   interface Driver extends
60       RequestFactoryEditorDriver<PersonProxy, PersonEditor> {
61   }
62 
register(EventBus eventBus, final DynaTableRequestFactory requestFactory, final FavoritesManager manager)63   static void register(EventBus eventBus,
64       final DynaTableRequestFactory requestFactory,
65       final FavoritesManager manager) {
66     eventBus.addHandler(EditPersonEvent.TYPE, new EditPersonEvent.Handler() {
67       public void startEdit(PersonProxy person, RequestContext requestContext) {
68         new PersonEditorWorkflow(requestFactory, manager, person).edit(requestContext);
69       }
70     });
71   }
72 
73   @UiField
74   HTMLPanel contents;
75 
76   @UiField
77   DialogBox dialog;
78 
79   @UiField
80   CheckBox favorite;
81 
82   @UiField(provided = true)
83   PersonEditor personEditor;
84 
85   private Driver editorDriver;
86   private final FavoritesManager manager;
87   private PersonProxy person;
88   private final DynaTableRequestFactory requestFactory;
89 
PersonEditorWorkflow(DynaTableRequestFactory requestFactory, FavoritesManager manager, PersonProxy person)90   private PersonEditorWorkflow(DynaTableRequestFactory requestFactory,
91       FavoritesManager manager, PersonProxy person) {
92     this.requestFactory = requestFactory;
93     this.manager = manager;
94     this.person = person;
95     TimeSlotListWidget timeSlotEditor = new TimeSlotListWidget(requestFactory);
96     ScheduleEditor scheduleEditor = new ScheduleEditor(timeSlotEditor);
97     MentorSelector mentorEditor = new MentorSelector(requestFactory);
98     personEditor = new PersonEditor(mentorEditor, scheduleEditor);
99     Binder.BINDER.createAndBindUi(this);
100     contents.addDomHandler(new KeyUpHandler() {
101       public void onKeyUp(KeyUpEvent event) {
102         if (event.getNativeKeyCode() == KeyCodes.KEY_ESCAPE) {
103           onCancel(null);
104         }
105       }
106     }, KeyUpEvent.getType());
107     this.favorite.setVisible(false);
108   }
109 
110   /**
111    * Called by the cancel button when it is clicked. This method will just tear
112    * down the UI and clear the state of the workflow.
113    */
114   @UiHandler("cancel")
onCancel(ClickEvent event)115   void onCancel(ClickEvent event) {
116     dialog.hide();
117   }
118 
119   /**
120    * Called by the edit dialog's save button. This method will flush the
121    * contents of the UI into the PersonProxy that is being edited, check for
122    * errors, and send the request to the server.
123    */
124   @UiHandler("save")
onSave(ClickEvent event)125   void onSave(ClickEvent event) {
126     // Flush the contents of the UI
127     RequestContext context = editorDriver.flush();
128 
129     // Check for errors
130     if (editorDriver.hasErrors()) {
131       dialog.setText("Errors detected locally");
132       return;
133     }
134 
135     // Send the request
136     context.fire(new Receiver<Void>() {
137       @Override
138       public void onConstraintViolation(Set<ConstraintViolation<?>> errors) {
139         // Otherwise, show ConstraintViolations in the UI
140         dialog.setText("Errors detected on the server");
141         editorDriver.setConstraintViolations(errors);
142       }
143 
144       @Override
145       public void onSuccess(Void response) {
146         // If everything went as planned, just dismiss the dialog box
147         dialog.hide();
148       }
149     });
150   }
151 
152   /**
153    * Called by the favorite checkbox when its value has been toggled.
154    */
155   @UiHandler("favorite")
onValueChanged(ValueChangeEvent<Boolean> event)156   void onValueChanged(ValueChangeEvent<Boolean> event) {
157     manager.setFavorite(person.stableId(), favorite.getValue());
158   }
159 
160   /**
161    * Construct and display the UI that will be used to edit the current
162    * PersonProxy, using the given RequestContext to accumulate the edits.
163    */
edit(RequestContext requestContext)164   private void edit(RequestContext requestContext) {
165     editorDriver = GWT.create(Driver.class);
166     editorDriver.initialize(requestFactory, personEditor);
167 
168     if (requestContext == null) {
169       this.favorite.setVisible(true);
170       fetchAndEdit();
171       return;
172     }
173 
174     editorDriver.edit(person, requestContext);
175     personEditor.focus();
176     favorite.setValue(manager.isFavorite(person), false);
177     dialog.center();
178   }
179 
fetchAndEdit()180   private void fetchAndEdit() {
181     // The request is configured arbitrarily
182     Request<PersonProxy> fetchRequest = requestFactory.find(person.stableId());
183 
184     // Add the paths that the EditorDrives computes
185     fetchRequest.with(editorDriver.getPaths());
186 
187     // We could do more with the request, but we just fire it
188     fetchRequest.to(new Receiver<PersonProxy>() {
189       @Override
190       public void onSuccess(PersonProxy person) {
191         PersonEditorWorkflow.this.person = person;
192         // Start the edit process
193         PersonRequest context = requestFactory.personRequest();
194         // Display the UI
195         edit(context);
196         // Configure the method invocation to be sent in the context
197         context.persist().using(person);
198         // The context will be fire()'ed from the onSave() method
199       }
200     }).fire();
201   }
202 }
203