Use light and dark icons

This commit is contained in:
Florian Hoss 2024-09-30 20:16:29 +02:00
parent 2f5d83cf12
commit 808cbdf115
Signed by: flohoss
GPG key ID: 3F35C7F6E6F66F6B
7 changed files with 98 additions and 35 deletions

View file

@ -4,9 +4,14 @@ import "gitlab.unjx.de/flohoss/godash/services"
templ Application(application services.Application) {
<a href={ templ.URL(application.URL) } class="flex items-center hover-effect">
<div class="w-8 h-8 flex items-center">
<div class={ "w-8", "h-8", "flex", templ.KV("dark:hidden", application.IconLight != ""), "items-center" }>
@templ.Raw(application.Icon)
</div>
if application.IconLight != "" {
<div class={ "w-8", "h-8", "hidden", "dark:flex", "items-center" }>
@templ.Raw(application.IconLight)
</div>
}
<div class="uppercase truncate ml-2">{ application.Name }</div>
</a>
}

View file

@ -10,10 +10,10 @@ func SetupRoutes(router *http.ServeMux, sse *sse.Server, appHandler *AppHandler,
router.Handle("GET /sse", authHandler.AuthMiddleware(http.HandlerFunc(sse.ServeHTTP)))
fsAssets := http.FileServer(http.Dir("assets"))
router.Handle("GET /assets/", authHandler.AuthMiddleware(http.StripPrefix("/assets/", fsAssets)))
router.Handle("GET /assets/", http.StripPrefix("/assets/", fsAssets))
icons := http.FileServer(http.Dir("storage/icons"))
router.Handle("GET /icons/", authHandler.AuthMiddleware(http.StripPrefix("/icons/", icons)))
router.Handle("GET /icons/", http.StripPrefix("/icons/", icons))
router.HandleFunc("GET /logout", authHandler.handleLogout)
router.HandleFunc("GET /callback", authHandler.handleCallback)

View file

@ -14,7 +14,7 @@ import (
"gitlab.unjx.de/flohoss/godash/handlers"
"gitlab.unjx.de/flohoss/godash/internal/env"
"gitlab.unjx.de/flohoss/godash/internal/logger"
"gitlab.unjx.de/flohoss/godash/pkg/logger"
"gitlab.unjx.de/flohoss/godash/services"
)

72
pkg/media/media.go Normal file
View file

@ -0,0 +1,72 @@
package media
import (
"fmt"
"io"
"io/fs"
"net/http"
"os"
"regexp"
"strings"
)
func DownloadSelfHostedIcon(ext, title, filePath string) ([]byte, error) {
resp, err := http.Get("https://cdn.jsdelivr.net/gh/selfhst/icons/" + strings.TrimPrefix(ext, ".") + "/" + title)
if err != nil {
return nil, fmt.Errorf("failed to get icon: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("failed to get icon, status: %d", resp.StatusCode)
}
data, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("failed to read icon: %w", err)
}
data = replaceClassNames(data, strings.TrimSuffix(title, ext))
data = insertWidthHeight(data)
err = os.WriteFile(filePath, data, fs.FileMode(0640))
if err != nil {
return nil, fmt.Errorf("failed to write icon: %w", err)
}
return data, nil
}
func insertWidthHeight(svgContent []byte) []byte {
classRegex := regexp.MustCompile(`(?:<svg(.+?)(width|height)=".+?")(.+?)(width|height)=".+?"|(<svg)`)
newSVGContent := classRegex.ReplaceAllFunc(svgContent, func(match []byte) []byte {
groups := classRegex.FindSubmatch(match)
if len(groups) == 0 {
return match
}
if string(match) == "<svg" {
return []byte(`<svg width="2rem" height="2rem" `)
} else {
return []byte(fmt.Sprintf(`<svg%s%s="2rem"%s%s="2rem"`, groups[1], groups[2], groups[3], groups[4]))
}
})
return newSVGContent
}
func replaceClassNames(svgContent []byte, title string) []byte {
// Regular expression to match either class="st0" or .st0
classRegex := regexp.MustCompile(`(class="|\.)([a-z]{2}\d)`)
newSVGContent := classRegex.ReplaceAllFunc(svgContent, func(match []byte) []byte {
groups := classRegex.FindSubmatch(match)
if len(groups) == 0 {
return match
}
group1 := string(groups[1])
group2 := string(groups[2])
if group1 == `class="` {
return []byte(`class="` + title + "-" + group2)
}
if group1 == `.` {
return []byte(`.` + title + "-" + group2)
}
return match
})
return newSVGContent
}

View file

@ -2,13 +2,12 @@ package services
import (
"io"
"io/fs"
"log/slog"
"net/http"
"os"
"path/filepath"
"strings"
"gitlab.unjx.de/flohoss/godash/pkg/media"
"gopkg.in/yaml.v3"
)
@ -98,34 +97,29 @@ func (bs *BookmarkService) replaceIconStrings() {
continue
}
data, err := os.ReadFile(iconsFolder + title)
if os.IsNotExist(err) {
if err != nil {
slog.Debug("icon not found, downloading...", "title", title)
resp, err := http.Get("https://cdn.jsdelivr.net/gh/selfhst/icons/" + strings.TrimPrefix(ext, ".") + "/" + title)
data, err = media.DownloadSelfHostedIcon(ext, title, iconsFolder+title)
if err != nil {
slog.Error("failed to get icon", "err", err.Error())
slog.Error(err.Error())
continue
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
slog.Error("failed to get icon", "status", resp.Status, "url", resp.Request.URL.String())
continue
}
data, err = io.ReadAll(resp.Body)
}
lightTitle := strings.Replace(title, ".svg", "-light.svg", 1)
lightData, err := os.ReadFile(iconsFolder + lightTitle)
if err != nil {
slog.Debug("light-icon not found, downloading...", "title", title)
lightData, err = media.DownloadSelfHostedIcon(ext, lightTitle, iconsFolder+lightTitle)
if err != nil {
slog.Error("failed to read icon", "err", err.Error())
continue
}
err = os.WriteFile(iconsFolder+title, data, fs.FileMode(0640))
if err != nil {
slog.Error("failed to write icon", "err", err.Error())
continue
slog.Warn(err.Error())
}
}
if data == nil {
slog.Error("icon data is null")
continue
}
bs.bookmarks.Applications[i].Entries[j].Icon = insertWidthHeight(string(data))
bs.bookmarks.Applications[i].Entries[j].Icon = string(data)
bs.bookmarks.Applications[i].Entries[j].IconLight = string(lightData)
}
}
}
@ -139,11 +133,3 @@ func (bs *BookmarkService) parseBookmarks() {
return
}
}
func insertWidthHeight(svg string) string {
parts := strings.SplitN(svg, "<svg", 2)
if len(parts) != 2 {
return svg
}
return parts[0] + "<svg width=\"2rem\" height=\"2rem\" " + parts[1]
}

View file

@ -21,8 +21,8 @@ type Link struct {
}
type Application struct {
Name string
Icon string
URL string
Light bool
Name string
Icon string
URL string
IconLight string
}