Skip to content

Commit b7e758c

Browse files
committed
PWA: Only emit maskable icons when the variant exists photoprism#5696
1 parent 4b33d3b commit b7e758c

8 files changed

Lines changed: 76 additions & 9 deletions

File tree

internal/config/config_app.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ func (c *Config) AppConfig() pwa.Config {
9898
BaseUri: c.BaseUri("/"),
9999
FrontendUri: c.FrontendUri(""),
100100
StaticUri: c.StaticUri(),
101+
StaticPath: c.StaticPath(),
101102
SiteUrl: c.SiteUrl(),
102103
CdnUrl: c.CdnUrl("/"),
103104
ThemeUri: ThemeUri,

internal/config/pwa/config.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ type Config struct {
1111
BaseUri string `json:"baseUri"`
1212
FrontendUri string `json:"frontendUri"`
1313
StaticUri string `json:"staticUri"`
14+
StaticPath string `json:"staticPath"`
1415
SiteUrl string `json:"siteUrl"`
1516
CdnUrl string `json:"cdnUrl"`
1617
ThemeUri string `json:"themeUri"`

internal/config/pwa/icon.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,14 @@ func NewIcons(c Config) Icons {
8989

9090
// Adaptive launcher icons generated from a safe-zone-padded source so Android
9191
// fills the home-screen mask shape instead of letterboxing the default icon.
92+
// Emitted only for sizes whose file exists on disk so the manifest never
93+
// advertises a maskable variant that resolves to a 404, e.g. for custom icon
94+
// sets without pre-rendered maskable images.
9295
for _, d := range MaskableIconSizes {
96+
if !maskableIconExists(c.StaticPath, appIcon, d) {
97+
continue
98+
}
99+
93100
icons = append(icons, Icon{
94101
Src: fmt.Sprintf("%s/icons/%s/maskable/%d.png", staticUri, appIcon, d),
95102
Sizes: fmt.Sprintf("%dx%d", d, d),
@@ -100,3 +107,16 @@ func NewIcons(c Config) Icons {
100107

101108
return icons
102109
}
110+
111+
// maskableIconExists reports whether a non-empty maskable icon file of the given
112+
// size exists on disk for the specified icon set. It returns false when staticPath
113+
// is unset, since the manifest must not reference variants that cannot be verified.
114+
func maskableIconExists(staticPath, appIcon string, size int) bool {
115+
if staticPath == "" {
116+
return false
117+
}
118+
119+
fileName := filepath.Join(staticPath, fs.IconsDir, appIcon, "maskable", fmt.Sprintf("%d.png", size))
120+
121+
return fs.FileExistsNotEmpty(fileName)
122+
}

internal/config/pwa/icon_test.go

Lines changed: 50 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import (
1010

1111
func TestNewIcons(t *testing.T) {
1212
t.Run("Standard", func(t *testing.T) {
13-
c := Config{StaticUri: "https://demo-cdn.photoprism.app/static", Icon: "test"}
13+
c := Config{StaticUri: "https://demo-cdn.photoprism.app/static", StaticPath: fs.Abs("./testdata"), Icon: "test"}
1414
result := NewIcons(c)
1515
assert.NotEmpty(t, result)
1616
assert.Len(t, result, len(IconSizes)+len(MaskableIconSizes))
@@ -20,21 +20,36 @@ func TestNewIcons(t *testing.T) {
2020
assert.Equal(t, "", result[0].Purpose)
2121
})
2222
t.Run("Maskable", func(t *testing.T) {
23-
c := Config{StaticUri: "https://demo-cdn.photoprism.app/static", Icon: "test"}
23+
c := Config{StaticUri: "https://demo-cdn.photoprism.app/static", StaticPath: fs.Abs("./testdata"), Icon: "test"}
2424
result := NewIcons(c)
25-
maskable := make(Icons, 0, len(MaskableIconSizes))
26-
for _, icon := range result {
27-
if icon.Purpose == "maskable" {
28-
maskable = append(maskable, icon)
29-
}
30-
}
25+
maskable := maskableIcons(result)
3126
assert.Len(t, maskable, len(MaskableIconSizes))
3227
assert.Equal(t, "https://demo-cdn.photoprism.app/static/icons/test/maskable/192.png", maskable[0].Src)
3328
assert.Equal(t, "192x192", maskable[0].Sizes)
3429
assert.Equal(t, "image/png", maskable[0].Type)
3530
assert.Equal(t, "https://demo-cdn.photoprism.app/static/icons/test/maskable/512.png", maskable[1].Src)
3631
assert.Equal(t, "512x512", maskable[1].Sizes)
3732
})
33+
t.Run("MaskablePartial", func(t *testing.T) {
34+
c := Config{StaticUri: "https://demo-cdn.photoprism.app/static", StaticPath: fs.Abs("./testdata"), Icon: "partial"}
35+
result := NewIcons(c)
36+
maskable := maskableIcons(result)
37+
assert.Len(t, maskable, 1)
38+
assert.Equal(t, "https://demo-cdn.photoprism.app/static/icons/partial/maskable/192.png", maskable[0].Src)
39+
assert.Len(t, result, len(IconSizes)+1)
40+
})
41+
t.Run("MaskableMissing", func(t *testing.T) {
42+
c := Config{StaticUri: "https://demo-cdn.photoprism.app/static", StaticPath: fs.Abs("./testdata"), Icon: "nomask"}
43+
result := NewIcons(c)
44+
assert.Empty(t, maskableIcons(result))
45+
assert.Len(t, result, len(IconSizes))
46+
})
47+
t.Run("StaticPathUnset", func(t *testing.T) {
48+
c := Config{StaticUri: "https://demo-cdn.photoprism.app/static", Icon: "test"}
49+
result := NewIcons(c)
50+
assert.Empty(t, maskableIcons(result))
51+
assert.Len(t, result, len(IconSizes))
52+
})
3853
t.Run("Custom", func(t *testing.T) {
3954
c := Config{StaticUri: "https://demo-cdn.photoprism.app/static", Icon: "/test.png"}
4055
result := NewIcons(c)
@@ -56,3 +71,30 @@ func TestNewIcons(t *testing.T) {
5671
assert.Equal(t, "", result[0].Purpose)
5772
})
5873
}
74+
75+
func TestMaskableIconExists(t *testing.T) {
76+
staticPath := fs.Abs("./testdata")
77+
t.Run("Exists", func(t *testing.T) {
78+
assert.True(t, maskableIconExists(staticPath, "test", 192))
79+
assert.True(t, maskableIconExists(staticPath, "test", 512))
80+
})
81+
t.Run("Missing", func(t *testing.T) {
82+
assert.False(t, maskableIconExists(staticPath, "test", 256))
83+
assert.False(t, maskableIconExists(staticPath, "partial", 512))
84+
assert.False(t, maskableIconExists(staticPath, "nomask", 192))
85+
})
86+
t.Run("StaticPathUnset", func(t *testing.T) {
87+
assert.False(t, maskableIconExists("", "test", 192))
88+
})
89+
}
90+
91+
// maskableIcons returns the subset of icons emitted with purpose "maskable".
92+
func maskableIcons(icons Icons) Icons {
93+
maskable := make(Icons, 0, len(MaskableIconSizes))
94+
for _, icon := range icons {
95+
if icon.Purpose == "maskable" {
96+
maskable = append(maskable, icon)
97+
}
98+
}
99+
return maskable
100+
}

internal/config/pwa/manifest_test.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,13 @@ import (
44
"testing"
55

66
"github.com/stretchr/testify/assert"
7+
8+
"github.com/photoprism/photoprism/pkg/fs"
79
)
810

911
func TestNewManifest(t *testing.T) {
1012
c := Config{
11-
Icon: "logo",
13+
Icon: "test",
1214
Color: "#aaaaaa",
1315
Name: "TestPrism+",
1416
Description: "App's Description",
@@ -17,6 +19,7 @@ func TestNewManifest(t *testing.T) {
1719
BaseUri: "/",
1820
FrontendUri: "/library",
1921
StaticUri: "/static",
22+
StaticPath: fs.Abs("./testdata"),
2023
}
2124

2225
t.Run("Standard", func(t *testing.T) {
9.04 KB
Loading
9.04 KB
Loading
28.6 KB
Loading

0 commit comments

Comments
 (0)