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}