1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 package info.magnolia.ui.vaadin.gwt.client.magnoliashell.shell;
35
36 import info.magnolia.ui.vaadin.gwt.client.dialog.connector.OverlayConnector;
37 import info.magnolia.ui.vaadin.gwt.client.magnoliashell.ShellState;
38 import info.magnolia.ui.vaadin.gwt.client.magnoliashell.event.ActivateAppEvent;
39 import info.magnolia.ui.vaadin.gwt.client.magnoliashell.event.AppRequestedEvent;
40 import info.magnolia.ui.vaadin.gwt.client.magnoliashell.event.CurrentAppCloseEvent;
41 import info.magnolia.ui.vaadin.gwt.client.magnoliashell.event.FullScreenEvent;
42 import info.magnolia.ui.vaadin.gwt.client.magnoliashell.event.HideShellAppsEvent;
43 import info.magnolia.ui.vaadin.gwt.client.magnoliashell.event.HideShellAppsRequestedEvent;
44 import info.magnolia.ui.vaadin.gwt.client.magnoliashell.event.ShellAppRequestedEvent;
45 import info.magnolia.ui.vaadin.gwt.client.magnoliashell.event.ShellAppStartedEvent;
46 import info.magnolia.ui.vaadin.gwt.client.magnoliashell.event.ShellAppStartingEvent;
47 import info.magnolia.ui.vaadin.gwt.client.magnoliashell.event.ShellAppsHiddenEvent;
48 import info.magnolia.ui.vaadin.gwt.client.magnoliashell.shell.rpc.ShellClientRpc;
49 import info.magnolia.ui.vaadin.gwt.client.magnoliashell.shell.rpc.ShellServerRpc;
50 import info.magnolia.ui.vaadin.gwt.client.magnoliashell.shellmessage.ShellMessageWidget.MessageType;
51 import info.magnolia.ui.vaadin.gwt.client.magnoliashell.viewport.connector.ViewportConnector;
52 import info.magnolia.ui.vaadin.gwt.client.magnoliashell.viewport.widget.AppsViewportWidget;
53 import info.magnolia.ui.vaadin.gwt.client.magnoliashell.viewport.widget.ViewportWidget;
54 import info.magnolia.ui.vaadin.gwt.client.shared.magnoliashell.Fragment;
55 import info.magnolia.ui.vaadin.gwt.client.shared.magnoliashell.ShellAppType;
56 import info.magnolia.ui.vaadin.gwt.client.shared.magnoliashell.ViewportType;
57 import info.magnolia.ui.vaadin.gwt.client.sticker.connector.StickerConnector;
58 import info.magnolia.ui.vaadin.magnoliashell.MagnoliaShell;
59
60 import java.util.Iterator;
61 import java.util.List;
62 import java.util.Map.Entry;
63 import java.util.logging.Level;
64 import java.util.logging.Logger;
65
66 import com.google.gwt.core.client.Scheduler;
67 import com.google.gwt.user.client.History;
68 import com.google.gwt.user.client.Window;
69 import com.google.gwt.user.client.ui.Widget;
70 import com.google.web.bindery.event.shared.EventBus;
71 import com.google.web.bindery.event.shared.SimpleEventBus;
72 import com.vaadin.client.BrowserInfo;
73 import com.vaadin.client.ComponentConnector;
74 import com.vaadin.client.ConnectorHierarchyChangeEvent;
75 import com.vaadin.client.Util;
76 import com.vaadin.client.communication.RpcProxy;
77 import com.vaadin.client.communication.StateChangeEvent.StateChangeHandler;
78 import com.vaadin.client.ui.AbstractLayoutConnector;
79 import com.vaadin.client.ui.layout.ElementResizeEvent;
80 import com.vaadin.client.ui.layout.ElementResizeListener;
81 import com.vaadin.client.ui.nativebutton.NativeButtonConnector;
82 import com.vaadin.shared.ui.Connect;
83
84 import elemental.client.Browser;
85
86
87
88
89 @Connect(MagnoliaShell.class)
90 public class MagnoliaShellConnector extends AbstractLayoutConnector implements MagnoliaShellView.Presenter {
91
92 private static final Logger log = Logger.getLogger(MagnoliaShellConnector.class.getName());
93
94 private ShellServerRpc rpc = RpcProxy.create(ShellServerRpc.class, this);
95 private MagnoliaShellView view;
96 private EventBus eventBus = new SimpleEventBus();
97 private Fragment lastHandledFragment;
98 private boolean isHistoryInitialized = false;
99
100 @Override
101 protected void init() {
102 super.init();
103 addStateChangeHandler((StateChangeHandler) event -> {
104 MagnoliaShellState state = getState();
105 Iterator<Entry<ShellAppType, Integer>> it = state.indications.entrySet().iterator();
106 while (it.hasNext()) {
107 final Entry<ShellAppType, Integer> entry = it.next();
108 view.setShellAppIndication(entry.getKey(), entry.getValue());
109 }
110
111 if (event.hasPropertyChanged("currentUriFragment")) {
112
113
114 if (!event.isInitialStateChange()) {
115 History.newItem(getState().currentUriFragment.toFragment(), true);
116 }
117 }
118 });
119
120 registerRpc(ShellClientRpc.class, new ShellClientRpc() {
121 @Override
122 public void showMessage(String type, String topic, String msg, String id) {
123 view.showMessage(MessageType.valueOf(type), topic, msg, id);
124 }
125
126 @Override
127 public void hideAllMessages() {
128 view.hideAllMessages();
129 }
130
131 @Override
132 public void setFullScreen(boolean isFullScreen) {
133 MagnoliaShellConnector.this.doFullScreen(isFullScreen);
134 }
135 });
136
137 getLayoutManager().addElementResizeListener(getWidget().getElement(), new ElementResizeListener() {
138 @Override
139 public void onElementResize(ElementResizeEvent e) {
140 view.updateShellDivet();
141 }
142 });
143
144 eventBus.addHandler(CurrentAppCloseEvent.TYPE, event -> closeCurrentApp());
145
146
147
148
149 eventBus.addHandler(ShellAppStartingEvent.TYPE, event -> {
150 ShellState.get().setShellAppStarting();
151 view.onShellAppStarting(event.getType());
152 });
153
154
155
156
157 eventBus.addHandler(ShellAppStartedEvent.TYPE, event -> {
158 final String currentHistoryToken = History.getToken();
159 final Fragment fragment = Fragment.fromString(currentHistoryToken);
160 String newShellAppName = event.getType().name();
161 ShellState.get().setShellAppStarted();
162 if (currentHistoryToken.isEmpty() || !fragment.isShellApp() || !fragment.getAppName().equalsIgnoreCase(newShellAppName)) {
163 final Fragment newFragment = Fragment.fromString("shell:" + newShellAppName.toLowerCase() + ":");
164 History.newItem(newFragment.toFragment(), false);
165 lastHandledFragment = newFragment;
166 activateShellApp(newFragment);
167 }
168 });
169
170
171
172
173
174 eventBus.addHandler(HideShellAppsRequestedEvent.TYPE, event -> {
175 if (ShellState.get().isShellAppStarted() || ShellState.get().isShellAppStarting()) {
176 onHideShellAppsRequested();
177 }
178 });
179
180
181
182
183 eventBus.addHandler(ShellAppsHiddenEvent.TYPE, event -> rpc.stopCurrentShellApp());
184
185
186
187
188 eventBus.addHandler(ActivateAppEvent.TYPE, event -> {
189 if (!ShellState.get().isShellAppStarting()) {
190 log.log(Level.WARNING, "Starting from swipe/keyboard: " + event.getName());
191 ShellState.get().setAppStarting();
192 rpc.activateApp(Fragment.fromString("app:" + event.getName()));
193 }
194 });
195
196
197
198
199 if (BrowserInfo.get().isIE11()) {
200 Browser.getWindow().setOnhashchange(event -> navigate());
201 } else {
202 Browser.getWindow().setOnpopstate(event -> navigate());
203 }
204 }
205
206 private void navigate() {
207 String hash = Window.Location.getHash();
208 if (hash.startsWith("#")) {
209 hash = hash.substring(1);
210 }
211
212 final Fragment newFragment = Fragment.fromString(hash);
213
214 if (newFragment.equals(lastHandledFragment)) {
215 return;
216 }
217
218 boolean hasActiveRequest = getConnection().getMessageSender().hasActiveRequest();
219
220 if (newFragment.isShellApp()) {
221 if (!hasActiveRequest || !ShellState.get().isAppStarting()) {
222 doShowShellApp(newFragment.resolveShellAppType());
223 }
224 } else {
225
226 if (lastHandledFragment != null) {
227 log.warning(
228 "App navigation from " +lastHandledFragment.toFragment()+ " to " + newFragment.toFragment() + (!newFragment.sameSubApp(lastHandledFragment) ? "" : "not") +", request will %s be sent");
229 }
230
231
232 final Fragment latestLoadedAppLocation = getState().currentUriFragment;
233
234
235
236
237
238 if (newFragment.isSameApp(latestLoadedAppLocation)) {
239 log.warning("Switching to 'APP STARTED' state since the updated app is already active");
240 ShellState.get().setAppStarted();
241 }
242
243
244
245
246
247
248 if (!hasActiveRequest || !newFragment.isSameApp(lastHandledFragment)) {
249 loadApp(newFragment.getAppName());
250 }
251
252 if (ShellState.get().isAppStarting() || !hasActiveRequest) {
253 rpc.activateApp(newFragment);
254 }
255 }
256
257 lastHandledFragment = newFragment;
258 }
259
260 @Override
261 public void activateShellApp(Fragment f) {
262 rpc.activateShellApp(f);
263 }
264
265 @Override
266 public void closeCurrentApp() {
267 ShellState.get().setAppClosing();
268 rpc.stopCurrentApp();
269 }
270
271 @Override
272 public void removeMessage(String id) {
273 rpc.removeMessage(id);
274 }
275
276 @Override
277 public void loadApp(String appName) {
278 view.onAppStarting();
279 eventBus.fireEvent(new AppRequestedEvent(appName));
280 }
281
282 private void doShowShellApp(ShellAppType shellAppType) {
283 eventBus.fireEvent(new ShellAppRequestedEvent(shellAppType));
284 }
285
286 private void doFullScreen(boolean isFullScreen) {
287 eventBus.fireEvent(new FullScreenEvent(isFullScreen));
288 }
289
290 @Override
291 public void onHideShellAppsRequested() {
292 final AppsViewportWidget/info/magnolia/ui/vaadin/gwt/client/magnoliashell/viewport/widget/AppsViewportWidget.html#AppsViewportWidget">AppsViewportWidget appViewportWidget = (AppsViewportWidget) ((ComponentConnector)getState().viewports.get(ViewportType.APP)).getWidget();
293
294
295 Widget currentApp = appViewportWidget.getCurrentApp();
296 if (currentApp != null) {
297 view.onAppStarting();
298 ShellState.get().setShellAppClosing();
299 eventBus.fireEvent(new HideShellAppsEvent());
300 } else {
301 doShowShellApp(ShellAppType.APPLAUNCHER);
302 }
303 }
304
305 @Override
306 public void showShellApp(ShellAppType type) {
307
308
309
310 if (!getConnection().getMessageSender().hasActiveRequest()) {
311 doShowShellApp(type);
312 }
313 }
314
315 @Override
316 public void updateCaption(ComponentConnector connector) {}
317
318 @Override
319 public void onConnectorHierarchyChange(ConnectorHierarchyChangeEvent event) {
320 List<ComponentConnector> children = getChildComponents();
321 for (ComponentConnector connector : children) {
322 if (connector instanceof ViewportConnector) {
323 final ViewportConnector./../../../../../info/magnolia/ui/vaadin/gwt/client/magnoliashell/viewport/connector/ViewportConnector.html#ViewportConnector">ViewportConnector vc = (ViewportConnector) connector;
324 view.updateViewport(vc.getWidget(), vc.getType());
325 vc.setEventBus(eventBus);
326 } else if (connector instanceof OverlayConnector) {
327 if (!view.hasOverlay(connector.getWidget())) {
328 final OverlayConnector../../../../../../info/magnolia/ui/vaadin/gwt/client/dialog/connector/OverlayConnector.html#OverlayConnector">OverlayConnector oc = (OverlayConnector) connector;
329 ComponentConnector overlayParent = (ComponentConnector) oc.getState().overlayParent;
330 Widget parentWidget = overlayParent.getWidget();
331 view.openOverlayOnWidget(oc.getWidget(), parentWidget);
332 }
333 } else if (connector instanceof NativeButtonConnector) {
334 view.setUserMenu(connector.getWidget());
335 } else if (connector instanceof StickerConnector) {
336 view.setSticker(connector.getWidget());
337 }
338 }
339
340 List<ComponentConnector> oldChildren = event.getOldChildren();
341 oldChildren.removeAll(children);
342 for (ComponentConnector cc : oldChildren) {
343 cc.getWidget().removeFromParent();
344 }
345 }
346
347 @Override
348 protected Widget createWidget() {
349 this.view = new MagnoliaShellViewImpl();
350 this.view.setPresenter(this);
351 return view.asWidget();
352 }
353
354 @Override
355 public MagnoliaShellState getState() {
356 return (MagnoliaShellState) super.getState();
357 }
358
359 @Override
360 public void updateViewportLayout(ViewportWidget activeViewport) {
361 getLayoutManager().setNeedsMeasure(Util.findConnectorFor(activeViewport));
362 getLayoutManager().layoutNow();
363 }
364
365
366 @Override
367 public void initHistory() {
368 Scheduler.get().scheduleDeferred(() -> {
369 if (!isHistoryInitialized) {
370 isHistoryInitialized = true;
371 navigate();
372 }
373 });
374 }
375
376 }