-
Notifications
You must be signed in to change notification settings - Fork 161
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Flow initial pwa #4334
Flow initial pwa #4334
Changes from 15 commits
74ccbf0
110a643
b03bdb0
1953c59
3538cbf
8908c3b
fc2a561
f52538a
5b0b1cc
10c19f1
97d580c
efeb58e
e19249a
b942b51
45f9931
96451c3
11454ad
3c4a794
e0ab2da
067fb12
2185806
a5ab9fe
c8b77e2
a73ebe4
9a44794
f7ad12c
d5d926f
ec3e500
eb57b39
3a74bd5
d8adc3f
ca2d2f4
6c4b20a
15a789f
663cd54
c71e058
682c3ad
0d67035
efb7050
b2bd0cd
3612a12
38c0460
e1fe75d
b4c69d0
ff85742
34eeaa7
6af74cd
4da44a9
689730a
a55390a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,321 @@ | ||
package com.vaadin.flow.dom; | ||
|
||
import javax.imageio.ImageIO; | ||
import java.awt.image.BufferedImage; | ||
import java.io.ByteArrayOutputStream; | ||
import java.io.IOException; | ||
import java.io.OutputStream; | ||
import java.io.Serializable; | ||
import java.util.Arrays; | ||
import java.util.HashMap; | ||
import java.util.Map; | ||
|
||
import org.jsoup.nodes.Element; | ||
|
||
/** | ||
* Implementation of icon -element. | ||
* | ||
* Creates the href automatically based on | ||
* - baseName (the file name with path, as "icons/icon.png" | ||
* - width (width of icon) | ||
* - height (height of icon) | ||
* - (possibly) fileHash (the hashcode of image file) | ||
* | ||
* The href will be set as: | ||
* [basename]-[width]x[height].png{?[filehash]} | ||
* | ||
* The trailing ?[filehash] will be added if icon cache is not controlled | ||
* by service worker: cached = false | ||
* | ||
* So caching of a icon is left left for browser if it's not cached with | ||
* service worker. | ||
* | ||
*/ | ||
public class Icon implements Serializable { | ||
/** | ||
* Where icon belongs to. | ||
* | ||
* In header or manifest.json. | ||
* | ||
*/ | ||
public enum Domain { | ||
HEADER, | ||
MANIFEST; | ||
} | ||
|
||
private boolean cached = false; | ||
private int width = 0; | ||
private int height = 0; | ||
private long fileHash = 0; | ||
private String baseName = "icons/icon.png"; | ||
private Domain domain = Domain.HEADER; | ||
private boolean hrefOverride = false; | ||
private byte[] data; | ||
|
||
private Map<String, String> attributes = new HashMap<>(); | ||
private String tag = "link"; | ||
|
||
public Icon() { | ||
attr("type", "image/png"); | ||
rel("icon"); | ||
} | ||
|
||
/** | ||
* Chaining setter for size. | ||
* | ||
* @param width width of icon | ||
* @param height height of icon | ||
* @return self | ||
*/ | ||
public Icon size(int width, int height) { | ||
this.width = width; | ||
this.height = height; | ||
|
||
attr("sizes", width + "x" + height); | ||
setRelativeName(); | ||
return this; | ||
} | ||
|
||
/** | ||
* Gets an {@link Element} presentation of the icon. | ||
* | ||
* @return an {@link Element} presentation of the icon | ||
*/ | ||
public Element asElement() { | ||
Element element = new Element(tag); | ||
attributes.entrySet().forEach(entry -> { | ||
element.attr(entry.getKey(), entry.getValue()); | ||
}); | ||
return element; | ||
} | ||
|
||
private Icon attr(String key, String value) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For readability of code. |
||
attributes.put(key, value); | ||
return this; | ||
} | ||
|
||
private String attr(String key) { | ||
return attributes.get(key); | ||
} | ||
|
||
/** | ||
* Width of icon. | ||
* | ||
* @return Width of icon | ||
*/ | ||
public int getWidth() { | ||
return width; | ||
} | ||
|
||
/** | ||
* Height of icon. | ||
* | ||
* @return Height of icon | ||
*/ | ||
public int getHeight() { | ||
return height; | ||
} | ||
|
||
/** | ||
* Should the icon be cached viá Service Worker. | ||
* | ||
* @return Should the icon be cached viá Service Worker. | ||
*/ | ||
public boolean cached() { | ||
return cached; | ||
} | ||
|
||
/** | ||
* Chained setter for chained. | ||
* | ||
* @param cached Should the icon cached viá Service Worker | ||
* @return | ||
*/ | ||
public Icon cached(boolean cached) { | ||
this.cached = cached; | ||
return this; | ||
} | ||
|
||
/** | ||
* Sets the href based on icon values. | ||
* | ||
*/ | ||
private void setRelativeName() { | ||
if (!hrefOverride) { | ||
int split = baseName.lastIndexOf("."); | ||
String link = baseName.substring(0,split) + "-" + sizes() + | ||
baseName.substring(split); | ||
if (!cached) { | ||
link = link + "?" + fileHash; | ||
} | ||
attr("href", link); | ||
} | ||
} | ||
|
||
/** | ||
* Gets string for sizes -attribute. | ||
* | ||
* | ||
* @return a String as [size]x[size] | ||
*/ | ||
public String sizes() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
return attr("sizes"); | ||
} | ||
|
||
/** | ||
* Chaining setter of href -attribute. | ||
* | ||
* Href is forced as relative, so all [./] -chars are removed from the | ||
* start of the href. | ||
* | ||
* Href is always set when either size or basename is set. | ||
* | ||
* @param href href | ||
* @return self | ||
*/ | ||
public Icon href(String href) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
hrefOverride = true; | ||
attr("href", href.replaceAll("^[\\./]+", "")); | ||
return this; | ||
} | ||
|
||
/** | ||
* href attribute. | ||
* | ||
* @return href attribute | ||
*/ | ||
public String href() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
return attr("href"); | ||
} | ||
|
||
/** | ||
* Return href with '/' -prefix and removed possible ?[fileHash]. | ||
* | ||
* Used in matching, when serving images. | ||
* | ||
* @return Return href with '/' -prefix and removed possible ?[fileHash] | ||
*/ | ||
public String relHref() { | ||
String[] splitted = href().split("\\?"); | ||
return "/" + splitted[0]; | ||
} | ||
|
||
/**' | ||
* Gets the cache-string used in Google Workbox caching. | ||
* | ||
* @return "{ url: '[href]', revision: '[fileHash' }" | ||
*/ | ||
public String cache() { | ||
return String.format("{ url: '%s', revision: '%s' }", href(), | ||
fileHash); | ||
} | ||
|
||
/** | ||
* Getter for rel attribute. | ||
* | ||
* @return rel attribute | ||
*/ | ||
public String rel() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
return attr("rel"); | ||
} | ||
|
||
/** | ||
* Chaining setter for rel-attribute. | ||
* | ||
* @param rel rel value | ||
* @return self | ||
*/ | ||
public Icon rel(String rel) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
attr("rel", rel); | ||
return this; | ||
} | ||
|
||
/** | ||
* Type attribute. | ||
* | ||
* @return type -attribute | ||
*/ | ||
public String type() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
return attr("type"); | ||
} | ||
|
||
/** | ||
* Chaining setter for domain. | ||
* | ||
* @param domain Domain | ||
* @return self | ||
*/ | ||
public Icon domain(Domain domain) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
this.domain = domain; | ||
return this; | ||
} | ||
|
||
/** | ||
* Domain of icon. | ||
* | ||
* @return Domain of icon | ||
*/ | ||
public Domain domain() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
return this.domain; | ||
} | ||
|
||
/** | ||
* Media attribute. | ||
* | ||
* @return Media attribute | ||
*/ | ||
public String media() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
return attr("media"); | ||
} | ||
|
||
/** | ||
* Chaining setter for media attribute. | ||
* | ||
* @param media media | ||
* @return self | ||
*/ | ||
public Icon media(String media) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
attr("media", media); | ||
return this; | ||
} | ||
|
||
/** | ||
* Chaining setter of basename. | ||
* | ||
* @param baseName image full name with path, like "icon/icon.png" | ||
* @return self | ||
*/ | ||
public Icon baseName(String baseName) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
this.baseName = baseName; | ||
setRelativeName(); | ||
return this; | ||
} | ||
|
||
/** | ||
* Sets the image presenting the icon. | ||
* | ||
* @param image image in png -format | ||
* @throws IOException | ||
*/ | ||
public void setImage(BufferedImage image) throws IOException { | ||
ByteArrayOutputStream baos = new ByteArrayOutputStream(); | ||
ImageIO.write( image, "png", baos ); | ||
baos.flush(); | ||
data = baos.toByteArray(); | ||
fileHash = Arrays.hashCode(data); | ||
setRelativeName(); | ||
baos.close(); | ||
} | ||
|
||
/** | ||
* Writes the image to output stream. | ||
* | ||
* @param outputStream output stream | ||
* @throws IOException | ||
*/ | ||
public synchronized void write(OutputStream outputStream) | ||
throws IOException { | ||
outputStream.write(data); | ||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -44,6 +44,7 @@ | |
import com.vaadin.flow.component.page.Inline; | ||
import com.vaadin.flow.component.page.Push; | ||
import com.vaadin.flow.component.page.Viewport; | ||
import com.vaadin.flow.dom.Icon; | ||
import com.vaadin.flow.function.DeploymentConfiguration; | ||
import com.vaadin.flow.internal.AnnotationReader; | ||
import com.vaadin.flow.internal.ReflectTools; | ||
|
@@ -570,6 +571,7 @@ private static void writeBootstrapPage(VaadinResponse response, String html) | |
private static List<Element> setupDocumentHead(Element head, | ||
BootstrapContext context) { | ||
setupMetaAndTitle(head, context); | ||
setupPWA(head); | ||
setupCss(head, context); | ||
|
||
JsonObject initialUIDL = getInitialUidl(context.getUI()); | ||
|
@@ -701,6 +703,42 @@ private static void setupMetaAndTitle(Element head, | |
}); | ||
} | ||
|
||
private static void setupPWA(Element head) { | ||
PWARegistry registry = VaadinService.getCurrent().getPwaRegistry(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Please pass There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done by Mikko. |
||
|
||
PwaConfiguration config = registry.getPwaConfiguration(); | ||
|
||
if (config.isEnabled()) { | ||
// Describe PWA capability for IOS devices | ||
head.appendElement(META_TAG) | ||
.attr("name", "apple-mobile-web-app-capable") | ||
.attr(CONTENT_ATTRIBUTE, "yes"); | ||
|
||
// Theme color | ||
head.appendElement(META_TAG) | ||
.attr("theme-color", config.getThemeColor()); | ||
head.appendElement(META_TAG) | ||
.attr("apple-mobile-web-app-status-bar-style", | ||
config.getThemeColor()); | ||
|
||
// Add manifest | ||
head.appendElement("link").attr("rel", "manifest") | ||
.attr("href", config.getManifestPath()); | ||
|
||
// Add icons | ||
for (Icon icon : registry.getHeaderIcons()) { | ||
head.appendChild(icon.asElement()); | ||
} | ||
|
||
// Add service worker initialization | ||
head.appendElement("script").text("if ('serviceWorker' in navigator) {\n" | ||
+ " window.addEventListener('load', function() {\n" | ||
+ " navigator.serviceWorker.register('" + | ||
config.getServiceWorkerPath() + "');\n" | ||
+ " });\n" + "}"); | ||
} | ||
} | ||
|
||
private static void appendWebComponentsPolyfills(Element head, | ||
BootstrapContext context) { | ||
VaadinSession session = context.getSession(); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
setSize instead of size
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Aye, there are chaining setters used in Icon with just property name for each did on purpose. Using setFoo with chaining seems not so good. I'll change those of course if needed. For instance to withFoo if it would be better? Or remove chaining totally, if it doesn't suite.