├── .gitignore ├── Procfile ├── README.md ├── package.json ├── pom.xml ├── serviceWorkerBuilder.js ├── src └── main │ ├── java │ └── com │ │ └── gwidgets │ │ ├── activities │ │ └── MainPageActivity.java │ │ ├── client │ │ ├── ClientFactory.java │ │ ├── ClientFactoryImpl.java │ │ ├── MyActivityMapper.java │ │ ├── MyHistoryMapper.java │ │ └── PwaDemo.java │ │ ├── places │ │ ├── BerlinPlace.java │ │ ├── LondonPlace.java │ │ └── ParisPlace.java │ │ ├── sw │ │ ├── Navigator.java │ │ └── ServiceWorkerContainer.java │ │ └── ui │ │ ├── MainAreaElement.java │ │ ├── Maps.java │ │ └── MenuElement.java │ ├── resources │ └── com │ │ └── gwidgets │ │ └── pwademo.gwt.xml │ └── webapp │ ├── WEB-INF │ └── web.xml │ ├── dev-imports.vulcanized.html │ ├── image │ ├── mapicon.png │ └── splash.png │ ├── manifest.json │ ├── pwademo.css │ ├── pwademo.html │ └── styles │ ├── app-theme.html │ └── shared-styles.html └── sw_template.js /.gitignore: -------------------------------------------------------------------------------- 1 | .classpath 2 | .settings 3 | target 4 | .project -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | 2 | web: java $JAVA_OPTS -jar target/dependency/webapp-runner.jar --port $PORT target/gwt-pwa-0.1 -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | ## Live Demo 3 | 4 | [https://gwt-pwa-demo.herokuapp.com/pwademo.html](https://gwt-pwa-demo.herokuapp.com/pwademo.html) 5 | 6 | ## Service worker generation 7 | 8 | service worker file (sw.js) is automatically generated during the maven build. Because GWT compiled files names are not known in advance, the generation of the service worker need to be done after the GWT compilation. The [frontend-maven-plugin](https://github.com/eirslett/frontend-maven-plugin) is used to install node and npm ( only inside the build folder, not globally), and execute `serviceWorkerBuilder.js` which goes through all the files inside the build folder and generates a `sw.js` with the resources ending in `.html`, `.js`, `.css`, `.gif`, `.png`, and `.jpeg`. -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gwt-pwa", 3 | "version": "0.0.1", 4 | "description": "generates service worker for GWT app", 5 | "main": "serviceWorkerBuilder.js", 6 | "dependencies": { 7 | "handlebars": "^4.0.11" 8 | }, 9 | "devDependencies": {}, 10 | "scripts": { 11 | "generate": "node serviceWorkerBuilder.js" 12 | }, 13 | "repository": { 14 | "type": "git", 15 | "url": "git+https://github.com/zak905/gwt-pwa-demo.git" 16 | }, 17 | "author": "zakaria amine", 18 | "license": "ISC", 19 | "bugs": { 20 | "url": "https://github.com/zak905/gwt-pwa-demo/issues" 21 | }, 22 | "homepage": "https://github.com/zak905/gwt-pwa-demo#readme" 23 | } 24 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 4.0.0 6 | com.gwidgets 7 | gwt-pwa 8 | war 9 | 0.1 10 | 11 | 12 | 13 | 14 | snapshots 15 | https://oss.sonatype.org/content/repositories/snapshots/ 16 | 17 | false 18 | 19 | 20 | true 21 | 22 | 23 | 24 | 25 | 26 | 2.8.2 27 | 1.7.0.0 28 | ${project.build.directory} 29 | ${project.build.directory}/${project.build.finalName} 30 | 1.8 31 | 1.8 32 | UTF-8 33 | 34 | 35 | 36 | 37 | com.google.gwt 38 | gwt-user 39 | ${gwtVersion} 40 | provided 41 | 42 | 43 | com.google.gwt 44 | gwt-dev 45 | ${gwtVersion} 46 | provided 47 | 48 | 49 | com.vaadin.polymer 50 | vaadin-gwt-polymer-elements 51 | ${gwtPolymerVersion} 52 | provided 53 | 54 | 55 | com.gwidgets 56 | gwty-leaflet 57 | 1.0-SNAPSHOT 58 | provided 59 | 60 | 61 | 62 | 63 | 64 | 65 | org.codehaus.mojo 66 | gwt-maven-plugin 67 | ${gwtVersion} 68 | 69 | 70 | 71 | compile 72 | test 73 | 74 | 75 | 76 | 77 | pwademo.html 78 | 79 | com.gwidgets.pwademo 80 | 81 | 1 82 | ${webappDirectory} 83 | ${war} 84 | true 85 | 86 | 87 | 88 | org.apache.maven.plugins 89 | maven-war-plugin 90 | 2.3 91 | 92 | 93 | compile 94 | 95 | 96 | 97 | ${webappDirectory} 98 | 99 | 100 | 101 | org.apache.maven.plugins 102 | maven-compiler-plugin 103 | 3.5.1 104 | 105 | 1.8 106 | 1.8 107 | 108 | 109 | 110 | org.apache.maven.plugins 111 | maven-dependency-plugin 112 | 2.3 113 | 114 | 115 | package 116 | copy 117 | 118 | 119 | 120 | com.github.jsimone 121 | webapp-runner 122 | 8.0.33.0 123 | webapp-runner.jar 124 | 125 | 126 | 127 | 128 | 129 | 130 | 151 | 152 | com.github.eirslett 153 | frontend-maven-plugin 154 | 1.6 155 | 156 | 157 | install node and npm 158 | 159 | install-node-and-npm 160 | 161 | generate-resources 162 | 163 | v9.0.0 164 | 165 | 166 | 167 | npm install 168 | 169 | npm 170 | 171 | generate-resources 172 | 173 | install 174 | 175 | 176 | 177 | npm generate 178 | 179 | npm 180 | 181 | prepare-package 182 | 183 | run-script generate ${project.build.finalName} 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | -------------------------------------------------------------------------------- /serviceWorkerBuilder.js: -------------------------------------------------------------------------------- 1 | const args = process.argv; 2 | const arguments = args.slice(2); 3 | 4 | const buildFolderName = arguments[0]; 5 | const gwtModuleName = arguments[1]; 6 | 7 | if (typeof buildFolderName === 'undefined' || !buildFolderName){ 8 | console.log("please provide the name of the build folder"); 9 | return; 10 | } 11 | 12 | const rootFolder = 'target/' + buildFolderName; 13 | const fs = require('fs'); 14 | var Handlebars = require('handlebars'); 15 | 16 | var filesToCache = []; 17 | 18 | 19 | browseAllFilesInDirectory(rootFolder); 20 | 21 | function browseAllFilesInDirectory(folder) { 22 | const filesRegExp = /\.(html|css|js|gif|png|jpeg)$/i; 23 | const exceptions = ["WEB-INF", "META-INF", "bower_components", "clear.cache.gif"]; 24 | 25 | fs.readdirSync(folder).forEach(fileName => { 26 | const resource = folder + "/" + fileName; 27 | if (exceptions.indexOf(fileName) < 0) { 28 | if (fileName.match(filesRegExp)) { 29 | filesToCache.push(resource.replace(rootFolder+"/", "")); 30 | } else if (fs.lstatSync(resource).isDirectory()) { 31 | browseAllFilesInDirectory(resource); 32 | } 33 | } 34 | }); 35 | } 36 | 37 | var swData = { 38 | cacheName: gwtModuleName || "cache_"+new Date().getTime().toString(), 39 | filesToCache: filesToCache, 40 | }; 41 | 42 | fs.readFile("sw_template.js", "utf8", (error, data) => { 43 | if (error) { 44 | console.log("Unable to read template file"); 45 | } 46 | var template = Handlebars.compile(data); 47 | var serviceWorkerJs = template(swData); 48 | 49 | fs.writeFile(rootFolder+"/sw.js", serviceWorkerJs, (error) => { 50 | console.log("successfully generated service worker sw.js in " + rootFolder); 51 | }); 52 | }); -------------------------------------------------------------------------------- /src/main/java/com/gwidgets/activities/MainPageActivity.java: -------------------------------------------------------------------------------- 1 | package com.gwidgets.activities; 2 | 3 | import com.google.gwt.activity.shared.AbstractActivity; 4 | import com.google.gwt.core.shared.GWT; 5 | import com.google.gwt.event.shared.EventBus; 6 | import com.google.gwt.place.shared.Place; 7 | import com.google.gwt.place.shared.PlaceController; 8 | import com.google.gwt.user.client.ui.AcceptsOneWidget; 9 | import com.gwidgets.client.ClientFactory; 10 | import com.gwidgets.places.ParisPlace; 11 | import com.gwidgets.places.BerlinPlace; 12 | import com.gwidgets.places.LondonPlace; 13 | import com.gwidgets.ui.MainAreaElement; 14 | import com.gwidgets.ui.MenuElement; 15 | 16 | public class MainPageActivity extends AbstractActivity{ 17 | 18 | MainAreaElement mainArea; 19 | MenuElement menu; 20 | PlaceController controller; 21 | 22 | public MainPageActivity(ClientFactory factory, Place place) { 23 | this.mainArea = factory.getMainAreaElement(); 24 | this.menu = factory.getMenuElement(); 25 | this.controller = factory.getPlaceController(); 26 | } 27 | 28 | @Override 29 | public void start(AcceptsOneWidget panel, EventBus bus) { 30 | 31 | 32 | } 33 | 34 | public void refreshPlace(Place place){ 35 | 36 | if(place instanceof BerlinPlace){ 37 | placeChangeWithoutClickEvent("berlin"); 38 | }else if(place instanceof ParisPlace){ 39 | placeChangeWithoutClickEvent("paris"); 40 | }else if(place instanceof LondonPlace){ 41 | placeChangeWithoutClickEvent("london"); 42 | } 43 | 44 | } 45 | 46 | public void placeChangeWithoutClickEvent(String placeName) { 47 | menu.getPaperMenu().select("contact"); 48 | mainArea.getIronPages().select(placeName); 49 | } 50 | 51 | 52 | 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/com/gwidgets/client/ClientFactory.java: -------------------------------------------------------------------------------- 1 | package com.gwidgets.client; 2 | 3 | import com.google.gwt.place.shared.PlaceController; 4 | import com.google.web.bindery.event.shared.EventBus; 5 | import com.gwidgets.ui.MainAreaElement; 6 | import com.gwidgets.ui.MenuElement; 7 | 8 | 9 | public interface ClientFactory { 10 | EventBus getEventBus(); 11 | PlaceController getPlaceController(); 12 | MenuElement getMenuElement(); 13 | MainAreaElement getMainAreaElement(); 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/com/gwidgets/client/ClientFactoryImpl.java: -------------------------------------------------------------------------------- 1 | package com.gwidgets.client; 2 | 3 | import com.google.gwt.place.shared.PlaceController; 4 | import com.google.web.bindery.event.shared.EventBus; 5 | import com.google.web.bindery.event.shared.SimpleEventBus; 6 | import com.gwidgets.ui.MainAreaElement; 7 | import com.gwidgets.ui.MenuElement; 8 | 9 | public class ClientFactoryImpl implements ClientFactory { 10 | 11 | 12 | EventBus eventBus = new SimpleEventBus(); 13 | PlaceController controller = new PlaceController(eventBus); 14 | 15 | MainAreaElement mainArea = new MainAreaElement(); 16 | MenuElement menu = new MenuElement(controller, mainArea); 17 | 18 | 19 | @Override 20 | public EventBus getEventBus() { 21 | return eventBus; 22 | } 23 | 24 | @Override 25 | public PlaceController getPlaceController() { 26 | return controller; 27 | } 28 | 29 | @Override 30 | public MenuElement getMenuElement() { 31 | return menu; 32 | } 33 | 34 | @Override 35 | public MainAreaElement getMainAreaElement() { 36 | return this.mainArea; 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/com/gwidgets/client/MyActivityMapper.java: -------------------------------------------------------------------------------- 1 | package com.gwidgets.client; 2 | 3 | import com.google.gwt.activity.shared.Activity; 4 | import com.google.gwt.activity.shared.ActivityMapper; 5 | import com.google.gwt.place.shared.Place; 6 | import com.gwidgets.activities.*; 7 | 8 | 9 | 10 | public class MyActivityMapper implements ActivityMapper { 11 | 12 | ClientFactory factory; 13 | 14 | MainPageActivity activity; 15 | 16 | 17 | 18 | public MyActivityMapper(ClientFactory factory ){ 19 | this.factory = factory; 20 | 21 | } 22 | 23 | @Override 24 | public Activity getActivity(Place place) { 25 | 26 | 27 | if(activity == null){ 28 | 29 | activity = new MainPageActivity(factory, place); 30 | }else{ 31 | 32 | activity.refreshPlace(place); 33 | } 34 | 35 | return activity; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/com/gwidgets/client/MyHistoryMapper.java: -------------------------------------------------------------------------------- 1 | package com.gwidgets.client; 2 | 3 | 4 | 5 | import com.google.gwt.place.shared.PlaceHistoryMapper; 6 | import com.google.gwt.place.shared.WithTokenizers; 7 | import com.gwidgets.places.*; 8 | 9 | 10 | @WithTokenizers({ParisPlace.Tokenizer.class, BerlinPlace.Tokenizer.class, LondonPlace.Tokenizer.class}) 11 | public interface MyHistoryMapper extends PlaceHistoryMapper { 12 | 13 | } 14 | 15 | -------------------------------------------------------------------------------- /src/main/java/com/gwidgets/client/PwaDemo.java: -------------------------------------------------------------------------------- 1 | package com.gwidgets.client; 2 | 3 | import com.google.gwt.activity.shared.ActivityManager; 4 | import com.google.gwt.activity.shared.ActivityMapper; 5 | import com.google.gwt.core.client.EntryPoint; 6 | import com.google.gwt.core.client.GWT; 7 | import com.google.gwt.core.client.JavaScriptObject; 8 | import com.google.gwt.place.shared.PlaceController; 9 | import com.google.gwt.place.shared.PlaceHistoryHandler; 10 | import com.google.gwt.user.client.ui.SimplePanel; 11 | import com.google.web.bindery.event.shared.EventBus; 12 | import com.gwidgets.api.leaflet.utils.LeafletResources; 13 | import com.gwidgets.places.BerlinPlace; 14 | import com.gwidgets.sw.Navigator; 15 | import com.vaadin.polymer.elemental.Function; 16 | 17 | public class PwaDemo implements EntryPoint { 18 | private BerlinPlace homePlace = new BerlinPlace("berlin"); 19 | private SimplePanel appWidget = new SimplePanel(); 20 | 21 | @Override 22 | public void onModuleLoad() { 23 | 24 | LeafletResources.whenReady(false, e -> { 25 | ClientFactory clientFactory = GWT.create(ClientFactory.class); 26 | PlaceController controller = clientFactory.getPlaceController(); 27 | 28 | EventBus bus = clientFactory.getEventBus(); 29 | ActivityMapper activityMapper = new MyActivityMapper(clientFactory); 30 | ActivityManager activityManager = new ActivityManager(activityMapper, bus); 31 | activityManager.setDisplay(appWidget); 32 | 33 | MyHistoryMapper historyMapper = GWT.create(MyHistoryMapper.class); 34 | final PlaceHistoryHandler historyHandler = new PlaceHistoryHandler(historyMapper); 35 | historyHandler.register(controller, bus, homePlace); 36 | 37 | historyHandler.handleCurrentHistory(); 38 | 39 | if (Navigator.serviceWorker != null) { 40 | Navigator.serviceWorker.register("sw.js") 41 | .then(new Function() { 42 | @Override 43 | public JavaScriptObject call(JavaScriptObject arg) { 44 | GWT.log("registred service worker successfully"); 45 | return null; 46 | } 47 | }); 48 | } else { 49 | GWT.log("service worker unavailable in this browser"); 50 | } 51 | 52 | return null; 53 | }); 54 | 55 | 56 | 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/com/gwidgets/places/BerlinPlace.java: -------------------------------------------------------------------------------- 1 | package com.gwidgets.places; 2 | 3 | import com.google.gwt.place.shared.Place; 4 | import com.google.gwt.place.shared.PlaceTokenizer; 5 | import com.google.gwt.place.shared.Prefix; 6 | 7 | public class BerlinPlace extends Place{ 8 | 9 | private String name; 10 | 11 | public BerlinPlace(String name) { 12 | this.name = name; 13 | } 14 | 15 | public String getName() { 16 | return name; 17 | } 18 | 19 | @Prefix("") 20 | public static class Tokenizer implements PlaceTokenizer { 21 | @Override 22 | public String getToken(BerlinPlace place) { 23 | return place.getName(); 24 | } 25 | 26 | @Override 27 | public BerlinPlace getPlace(String token) { 28 | return new BerlinPlace(token); 29 | } 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/com/gwidgets/places/LondonPlace.java: -------------------------------------------------------------------------------- 1 | package com.gwidgets.places; 2 | 3 | import com.google.gwt.place.shared.Place; 4 | import com.google.gwt.place.shared.PlaceTokenizer; 5 | import com.google.gwt.place.shared.Prefix; 6 | 7 | public class LondonPlace extends Place{ 8 | 9 | 10 | private String name; 11 | 12 | public LondonPlace(String name) { 13 | this.name = name; 14 | } 15 | 16 | public String getName() { 17 | return name; 18 | } 19 | 20 | @Prefix("�") 21 | public static class Tokenizer implements PlaceTokenizer { 22 | @Override 23 | public String getToken(LondonPlace place) { 24 | return place.getName(); 25 | } 26 | 27 | @Override 28 | public LondonPlace getPlace(String token) { 29 | return new LondonPlace(token); 30 | } 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/com/gwidgets/places/ParisPlace.java: -------------------------------------------------------------------------------- 1 | package com.gwidgets.places; 2 | 3 | import com.google.gwt.place.shared.Place; 4 | import com.google.gwt.place.shared.PlaceTokenizer; 5 | import com.google.gwt.place.shared.Prefix; 6 | 7 | 8 | public class ParisPlace extends Place { 9 | 10 | private String name; 11 | 12 | public ParisPlace(String name) { 13 | this.name = name; 14 | } 15 | 16 | public String getName() { 17 | return name; 18 | } 19 | 20 | @Prefix("!") 21 | public static class Tokenizer implements PlaceTokenizer { 22 | @Override 23 | public String getToken(ParisPlace place) { 24 | return place.getName(); 25 | } 26 | 27 | @Override 28 | public ParisPlace getPlace(String token) { 29 | return new ParisPlace(token); 30 | } 31 | } 32 | 33 | 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/com/gwidgets/sw/Navigator.java: -------------------------------------------------------------------------------- 1 | package com.gwidgets.sw; 2 | 3 | import jsinterop.annotations.JsProperty; 4 | import jsinterop.annotations.JsType; 5 | import static jsinterop.annotations.JsPackage.GLOBAL; 6 | 7 | @JsType(isNative=true, namespace= GLOBAL, name="navigator") 8 | public class Navigator { 9 | 10 | @JsProperty 11 | public static ServiceWorkerContainer serviceWorker; 12 | 13 | 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/com/gwidgets/sw/ServiceWorkerContainer.java: -------------------------------------------------------------------------------- 1 | package com.gwidgets.sw; 2 | 3 | import com.vaadin.polymer.elemental.Promise; 4 | 5 | import jsinterop.annotations.JsMethod; 6 | import jsinterop.annotations.JsType; 7 | 8 | @JsType(isNative=true, name="serviceWorker") 9 | public class ServiceWorkerContainer { 10 | 11 | @JsMethod 12 | public native String toString(); 13 | 14 | @JsMethod 15 | public native Promise register(String scriptName); 16 | 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/com/gwidgets/ui/MainAreaElement.java: -------------------------------------------------------------------------------- 1 | package com.gwidgets.ui; 2 | 3 | import com.google.gwt.core.client.GWT; 4 | import com.google.gwt.core.client.RunAsyncCallback; 5 | import com.google.gwt.dom.client.Document; 6 | import com.vaadin.polymer.Polymer; 7 | import com.vaadin.polymer.iron.IronPagesElement; 8 | 9 | public class MainAreaElement { 10 | 11 | IronPagesElement ironPages; 12 | 13 | boolean londonMapInitialized = false; 14 | 15 | boolean parisMapInitialized = false; 16 | 17 | public MainAreaElement(){ 18 | 19 | ironPages = (IronPagesElement) Polymer.getDocument().getElementById("ironPages"); 20 | 21 | loadStartupMap(); 22 | 23 | 24 | //Maps are not loaded on start up, but only when iron selector selects a new map 25 | ironPages.addEventListener("iron-select", e -> { 26 | 27 | if(ironPages.getSelected().equals("london") && !londonMapInitialized){ 28 | 29 | //Some code splitting to reduce initial module size 30 | GWT.runAsync(new RunAsyncCallback(){ 31 | @Override 32 | public void onFailure(Throwable reason) { 33 | Document.get().getElementById("londonMap").setInnerHTML("Could not load this map, please try again later"); 34 | } 35 | 36 | @Override 37 | public void onSuccess() { 38 | Maps.initializeLondonMap(); 39 | 40 | }}); 41 | londonMapInitialized = true; 42 | }else if(ironPages.getSelected().equals("paris") && !parisMapInitialized){ 43 | //Some code splitting to reduce initial module size 44 | GWT.runAsync(new RunAsyncCallback(){ 45 | 46 | @Override 47 | public void onFailure(Throwable reason) { 48 | Document.get().getElementById("parisMap").setInnerHTML("Could not load this map, please try again later"); 49 | } 50 | 51 | @Override 52 | public void onSuccess() { 53 | Maps.initializeParisMap(); 54 | } 55 | 56 | 57 | }); 58 | parisMapInitialized = true; 59 | } 60 | }); 61 | 62 | 63 | } 64 | 65 | public IronPagesElement getIronPages() { 66 | return this.ironPages; 67 | } 68 | 69 | 70 | private void loadStartupMap(){ 71 | 72 | Maps.initializeBerlinMap(); 73 | 74 | 75 | } 76 | 77 | } 78 | -------------------------------------------------------------------------------- /src/main/java/com/gwidgets/ui/Maps.java: -------------------------------------------------------------------------------- 1 | package com.gwidgets.ui; 2 | 3 | import com.gwidgets.api.leaflet.L; 4 | import com.gwidgets.api.leaflet.Map; 5 | 6 | public class Maps { 7 | 8 | public static final String MAP_URL = "https://api.mapbox.com/styles/v1/g-widgets/ciqy0cpax0012cfm8vx7ptrz9/tiles/256/{z}/{x}/{y}" 9 | + "?access_token=pk.eyJ1IjoiZy13aWRnZXRzIiwiYSI6ImNpcXV5YzdyajAwNmRoeG0zbm80c3d4Y3IifQ.eyVA2x_DJG_bzDObhnjaTA"; 10 | 11 | public static void initializeBerlinMap() { 12 | 13 | Map berlin = L.map("berlinMap", null); 14 | 15 | L.tileLayer(MAP_URL, null).addTo(berlin); 16 | berlin.setView(L.latLng(52.51, 13.40), 12, null); 17 | 18 | } 19 | 20 | public static void initializeParisMap() { 21 | 22 | 23 | 24 | Map paris = L.map("parisMap", null); 25 | 26 | 27 | L.tileLayer(MAP_URL, null).addTo(paris); 28 | 29 | paris.setView(L.latLng(48.876905, 2.376050), 12, null); 30 | 31 | 32 | } 33 | 34 | public static void initializeLondonMap() { 35 | 36 | Map london = L.map("londonMap", null); 37 | 38 | L.tileLayer(MAP_URL, null).addTo(london); 39 | london.setView(L.latLng(51.518112, -0.070906), 12, null); 40 | 41 | 42 | } 43 | 44 | 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/com/gwidgets/ui/MenuElement.java: -------------------------------------------------------------------------------- 1 | package com.gwidgets.ui; 2 | 3 | import com.google.gwt.dom.client.AnchorElement; 4 | import com.google.gwt.dom.client.Document; 5 | import com.google.gwt.place.shared.PlaceController; 6 | import com.google.gwt.user.client.Event; 7 | import com.gwidgets.places.ParisPlace; 8 | import com.gwidgets.places.BerlinPlace; 9 | import com.gwidgets.places.LondonPlace; 10 | import com.vaadin.polymer.Polymer; 11 | import com.vaadin.polymer.paper.PaperMenuElement; 12 | 13 | public class MenuElement { 14 | 15 | PaperMenuElement paperMenu; 16 | 17 | AnchorElement paris; 18 | 19 | AnchorElement berlin; 20 | 21 | AnchorElement london; 22 | 23 | PlaceController controller; 24 | 25 | MainAreaElement main; 26 | 27 | public MenuElement(PlaceController controller, MainAreaElement main){ 28 | 29 | this.main = main; 30 | 31 | this.controller = controller; 32 | 33 | paperMenu = (PaperMenuElement) Polymer.getDocument().getElementById("paperMenu"); 34 | 35 | paris = (AnchorElement) Document.get().getElementById("parisLink"); 36 | 37 | berlin = (AnchorElement) Document.get().getElementById("berlinLink"); 38 | 39 | london = (AnchorElement) Document.get().getElementById("londonLink"); 40 | 41 | initializeEvents(); 42 | } 43 | 44 | 45 | public void initializeEvents(){ 46 | 47 | 48 | Event.sinkEvents(paris, Event.ONCLICK); 49 | Event.sinkEvents(berlin, Event.ONCLICK); 50 | Event.sinkEvents(london, Event.ONCLICK); 51 | 52 | Event.setEventListener(paris, e -> { 53 | if(Event.ONCLICK == e.getTypeInt()) { 54 | main.getIronPages().select("paris"); 55 | paperMenu.select("paris"); 56 | controller.goTo(new ParisPlace("paris")); 57 | 58 | } 59 | }); 60 | 61 | Event.setEventListener(berlin, e -> { 62 | if(Event.ONCLICK == e.getTypeInt()) { 63 | main.getIronPages().select("berlin"); 64 | paperMenu.select("berlin"); 65 | controller.goTo(new BerlinPlace("berlin")); 66 | } 67 | }); 68 | 69 | Event.setEventListener(london, e -> { 70 | if(Event.ONCLICK == e.getTypeInt()) { 71 | main.getIronPages().select("london"); 72 | paperMenu.select("london"); 73 | controller.goTo(new LondonPlace("london")); 74 | 75 | } 76 | }); 77 | 78 | } 79 | 80 | 81 | public PaperMenuElement getPaperMenu() { 82 | return this.paperMenu; 83 | } 84 | 85 | 86 | 87 | 88 | } 89 | -------------------------------------------------------------------------------- /src/main/resources/com/gwidgets/pwademo.gwt.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/main/webapp/WEB-INF/web.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | pwademo.html 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/main/webapp/image/mapicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gwidgets/gwt-pwa-demo/ec7e20107a7ee2c970561651a4a42e58b8f0af3e/src/main/webapp/image/mapicon.png -------------------------------------------------------------------------------- /src/main/webapp/image/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gwidgets/gwt-pwa-demo/ec7e20107a7ee2c970561651a4a42e58b8f0af3e/src/main/webapp/image/splash.png -------------------------------------------------------------------------------- /src/main/webapp/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Favorite Maps PWA", 3 | "short_name": "Favorite Maps PWA", 4 | "icons": [{ 5 | "src": "image/mapicon.png", 6 | "sizes": "144x144", 7 | "type": "image/png" 8 | }, 9 | { 10 | "src": "image/splash.png", 11 | "sizes": "348x348", 12 | "type": "image/png" 13 | } 14 | ], 15 | "start_url": "/pwademo.html", 16 | "display": "standalone", 17 | "background_color": "#3E4EB8", 18 | "theme_color": "#2E3AA1" 19 | } 20 | -------------------------------------------------------------------------------- /src/main/webapp/pwademo.css: -------------------------------------------------------------------------------- 1 | 2 | body{ 3 | background: #ececec; 4 | } 5 | 6 | .mapContainer{ 7 | max-height:400px; 8 | height:100%; 9 | width:100%; 10 | max-width: 500px; 11 | } 12 | 13 | 14 | .section{ 15 | margin: auto; 16 | max-width: 500px; 17 | width: 100%; 18 | height: 100%; 19 | max-height:450px; 20 | } -------------------------------------------------------------------------------- /src/main/webapp/pwademo.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | GWT progressive web app demo 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 25 | 26 |
27 | 28 | 29 | 30 | 31 | 32 | Maps 34 | Paris 36 | Berlin 38 | London 40 | 42 | 44 | 46 | 47 |
48 |
My Favorite Maps
49 |
50 |
51 |
52 | 54 |
55 | 56 |
57 |
58 |
59 |
60 | 61 |
62 |
63 |
64 |
65 | 66 |
67 |
68 |
69 |
70 |
71 |
72 | 73 | 74 |
75 | 76 | 77 | -------------------------------------------------------------------------------- /src/main/webapp/styles/app-theme.html: -------------------------------------------------------------------------------- 1 | 236 | -------------------------------------------------------------------------------- /src/main/webapp/styles/shared-styles.html: -------------------------------------------------------------------------------- 1 | 2 | 20 | 21 | -------------------------------------------------------------------------------- /sw_template.js: -------------------------------------------------------------------------------- 1 | var cacheName = '{{cacheName}}'; 2 | var filesToCache = [ {{#each filesToCache}} 3 | '{{.}}'{{#unless @last}},{{/unless}} 4 | {{/each}} 5 | ]; 6 | 7 | 8 | self.addEventListener('install', function(e) { 9 | console.log('[ServiceWorker] Install'); 10 | e.waitUntil( 11 | caches.open(cacheName).then(function(cache) { 12 | console.log('[ServiceWorker] Caching app shell'); 13 | return cache.addAll(filesToCache); 14 | }) 15 | ); 16 | }); 17 | 18 | 19 | self.addEventListener('activate', function(e) { 20 | console.log('[ServiceWorker] Activate'); 21 | e.waitUntil( 22 | caches.keys().then(function(keyList) { 23 | return Promise.all(keyList.map(function(key) { 24 | console.log('[ServiceWorker] Removing old cache', key); 25 | if (key !== cacheName) { 26 | return caches.delete(key); 27 | } 28 | })); 29 | }) 30 | ); 31 | }); 32 | 33 | self.addEventListener('fetch', function(e) { 34 | console.log('[ServiceWorker] Fetch', e.request.url); 35 | e.respondWith( 36 | caches.match(e.request).then(function(response) { 37 | return response || fetch(e.request); 38 | }) 39 | ); 40 | }); --------------------------------------------------------------------------------