001package votorola.s.gwt.stage.talk; // Copyright 2012, Christian Weilbach. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Votorola Software"), to deal in the Votorola Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicence, and/or sell copies of the Votorola Software, and to permit persons to whom the Votorola Software is furnished to do so, subject to the following conditions: The preceding copyright notice and this permission notice shall be included in all copies or substantial portions of the Votorola Software. THE VOTOROLA SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE VOTOROLA SOFTWARE OR THE USE OR OTHER DEALINGS IN THE VOTOROLA SOFTWARE. 002 003import votorola.a.web.gwt.App; 004import votorola.g.hold.Hold; 005import votorola.g.hold.Spool; 006import votorola.g.hold.Spool1; 007import votorola.g.web.gwt.GWTX; 008import votorola.g.web.gwt.event.Change; 009import votorola.g.web.gwt.event.ChangeHandler; 010import votorola.g.web.gwt.event.PropertyChange; 011import votorola.g.web.gwt.event.PropertyChangeHandler; 012import votorola.s.gwt.stage.Stage; 013import votorola.s.gwt.stage.StageV; 014import votorola.s.gwt.stage.TheatreInitializer0; 015import votorola.s.gwt.stage.Track; 016 017import com.google.gwt.core.client.JavaScriptObject; 018import com.google.gwt.core.client.JsArray; 019import com.google.gwt.event.shared.GwtEvent; 020import com.google.gwt.event.shared.HasHandlers; 021import com.google.gwt.user.client.Timer; 022import com.google.gwt.user.client.rpc.AsyncCallback; 023import com.google.gwt.user.client.ui.Widget; 024import com.google.web.bindery.event.shared.HandlerRegistration; 025 026/** 027 * A track model for showing recent communications. 028 * 029 * @see TalkTrackV 030 */ 031public final class TalkTrack implements HasHandlers, Hold, Track { 032 private final Spool spool = new Spool1(); 033 034 private JsArray<DiffMessageJS> msgs; 035 036 /** 037 * Get the current array of messages. Used by the view 038 * after it is notified via a {@linkplain Change} event. 039 * 040 * @return messages 041 */ 042 public JsArray<DiffMessageJS> messages() { 043 return msgs; 044 } 045 046 private final Loader loader = new Loader(); 047 private final Kicker kicker = new Kicker(); 048 049 /** 050 * Constructs a TalkTrack. Call {@linkplain #release release}() when done 051 * with it. 052 */ 053 public TalkTrack() { 054 Stage.i().addInitializer(new TheatreInitializer0() // auto-removed 055 { 056 @Override 057 public void initFromComplete(final Stage s, 058 final boolean rPending) { 059 init(s); 060 } 061 062 @Override 063 public void initToComplete(final Stage s, 064 final boolean rPending) { 065 init(s); 066 } 067 }); 068 } 069 070 private void init(Stage s) { 071 loader.load(); 072 } 073 074 private TalkTrackV talkTrackV; // final after newView 075 076 /** 077 * Create a new view for the stage. This constructs a 078 */ 079 @Override 080 public Widget newView(final StageV stageV) { 081 assert talkTrackV == null; 082 talkTrackV = new TalkTrackV(this, stageV); 083 084 spool.add(new Hold() { 085 final HandlerRegistration hR = GWTX.i().bus() 086 .addHandlerToSource(Change.TYPE, /* source */ 087 talkTrackV, viewHandler); 088 089 public void release() { 090 hR.removeHandler(); 091 } 092 }); 093 094 spool.add(new Hold() { 095 final HandlerRegistration hR = GWTX 096 .i() 097 .bus() 098 .addHandlerToSource(PropertyChange.TYPE, Stage.i(), 099 stageHandler); 100 101 @Override 102 public void release() { 103 hR.removeHandler(); 104 } 105 106 }); 107 108 return talkTrackV; 109 } 110 111 public void fireEvent(final GwtEvent<?> e) { 112 assert e instanceof Change; 113 GWTX.i().bus().fireEventFromSource(e, TalkTrack.this); 114 } 115 116 private final PropertyChangeHandler stageHandler = new PropertyChangeHandler() { 117 118 @Override 119 public void onPropertyChange(PropertyChange e) { 120 if (e.propertyName().equals("pollName") 121 || e.propertyName().equals("actorName")) { 122 loader.load(); 123 } 124 } 125 126 }; 127 128 /** 129 * Triggered after view is loaded and initializes the view with a first 130 * fetched. 131 */ 132 private final ChangeHandler viewHandler = new ChangeHandler() { 133 134 @Override 135 public void onChange(Change e) { 136 kicker.kick(); 137 } 138 }; 139 140 /** 141 * Querying the database for messages for a pollname filtered by timeframe 142 * and user. Prefix 'h' hardcoded atm. 143 */ 144 private final class Loader implements AsyncCallback<JavaScriptObject> { 145 146 // callback parameter added automatically by JsonpRequestBuilder 147 private final String REQUEST_URL = App.getServletContextLocation() 148 + "/wap?wCall=hHarvest"; 149 150 public void load() { 151 if (spool.isUnwinding()) 152 return; // though probably impossible, as ticker would be 153 // cancelled 154 155 final StringBuilder ub = new StringBuilder(); 156 ub.append(REQUEST_URL); 157 if (Stage.i().getPollName() != null) { 158 ub.append("&hPoll=").append(Stage.i().getPollName()); 159 } 160 161 final String actor = Stage.i().getActorName(); 162 if (actor != null) { 163 ub.append("&hUsers=").append(Stage.i().getActorName()); 164 final String defaultActor = Stage.i().getDefaultActorName(); 165 if(defaultActor != null && !defaultActor.equals(actor)) { 166 ub.append(",").append(defaultActor); 167 } 168 } 169 App.i().jsonpWAP().requestObject(ub.toString(), Loader.this); 170 } 171 172 @Override 173 public void onFailure(Throwable caught) { 174 Stage.i() 175 .addWarning( 176 "Could not fetch messages: " 177 + caught.getLocalizedMessage()); 178 } 179 180 @Override 181 public void onSuccess(JavaScriptObject result) { 182 if (spool.isUnwinding()) 183 return; 184 185 msgs = stripPrefix(result); 186 fireEvent(new Change()); 187 } 188 189 private final native JsArray<DiffMessageJS> stripPrefix( 190 JavaScriptObject prefixJs) 191 /*-{ 192 return prefixJs["h"]; 193 }-*/; 194 } 195 196 /** 197 * Querying the database for messages for a pollname filtered by timeframe 198 * and user. Prefix 'h' hardcoded atm. 199 */ 200 private final class Kicker implements AsyncCallback<JavaScriptObject> { 201 202 // callback parameter added automatically by JsonpRequestBuilder 203 private final String REQUEST_URL = App.getServletContextLocation() 204 + "/wap?wCall=kKick"; 205 206 public void kick() { 207 if (spool.isUnwinding()) 208 return; // though probably impossible, as ticker would be 209 // cancelled 210 211 if (Stage.i().getPollName() == null 212 || Stage.i().getActorName() == null) { 213 return; 214 } 215 216 final StringBuilder ub = new StringBuilder(); 217 ub.append(REQUEST_URL); 218 ub.append("&kPoll=").append(Stage.i().getPollName()); 219 ub.append("&kUser=").append(Stage.i().getActorName()); 220 App.i().jsonpWAP().requestObject(ub.toString(), Kicker.this); 221 } 222 223 @Override 224 public void onFailure(Throwable caught) { 225 Stage.i() 226 .addWarning( 227 "Could not kick the harvesters: " 228 + caught.getLocalizedMessage()); 229 } 230 231 @Override 232 public void onSuccess(JavaScriptObject result) { 233 if (spool.isUnwinding()) 234 return; 235 236 if (stripPrefix(result)._getBoolean("triggered")) { 237 new Timer() { 238 @Override 239 public void run() { 240 loader.load(); 241 } 242 243 }.schedule(10 * 1000); 244 } else { 245 // update the view immediately, data has not changed 246 // so UpdateButton will be properly updated. 247 fireEvent(new Change()); 248 } 249 250 } 251 252 private final native JsArray<DiffMessageJS> stripPrefix( 253 JavaScriptObject prefixJs) 254 /*-{ 255 return prefixJs["k"]; 256 }-*/; 257 } 258 259 @Override 260 public void release() { 261 spool.unwind(); 262 } 263 264}