diff --git a/server/web/web.go b/server/web/web.go index 14fa98bb8..898c013fe 100644 --- a/server/web/web.go +++ b/server/web/web.go @@ -37,7 +37,8 @@ func New() *gin.Engine { e.Use(setupCache) h := http.FileServer(web.HTTPFS()) - e.GET("/favicon.svg", gin.WrapH(h)) + e.GET("/favicon.svg", redirect("/favicons/favicon-light-default.svg", http.StatusPermanentRedirect)) + e.GET("/favicons/*filepath", gin.WrapH(h)) e.GET("/assets/*filepath", gin.WrapH(h)) e.NoRoute(handleIndex) @@ -45,6 +46,18 @@ func New() *gin.Engine { return e } +// redirect return gin helper to redirect a request +func redirect(location string, status ...int) func(ctx *gin.Context) { + return func(ctx *gin.Context) { + code := http.StatusFound + if len(status) == 1 { + code = status[0] + } + + http.Redirect(ctx.Writer, ctx.Request, location, code) + } +} + func handleIndex(c *gin.Context) { rw := c.Writer data := web.MustLookup("index.html") diff --git a/web/index.html b/web/index.html index 263a92f7a..fecf3cb87 100644 --- a/web/index.html +++ b/web/index.html @@ -2,7 +2,8 @@ - + + Woodpecker diff --git a/web/public/favicons/favicon-dark-default.png b/web/public/favicons/favicon-dark-default.png new file mode 100644 index 000000000..b3a29a2ee Binary files /dev/null and b/web/public/favicons/favicon-dark-default.png differ diff --git a/web/public/favicons/favicon-dark-default.svg b/web/public/favicons/favicon-dark-default.svg new file mode 100644 index 000000000..79b2d60f8 --- /dev/null +++ b/web/public/favicons/favicon-dark-default.svg @@ -0,0 +1 @@ + diff --git a/web/public/favicons/favicon-dark-error.png b/web/public/favicons/favicon-dark-error.png new file mode 100644 index 000000000..f83f4a374 Binary files /dev/null and b/web/public/favicons/favicon-dark-error.png differ diff --git a/web/public/favicons/favicon-dark-error.svg b/web/public/favicons/favicon-dark-error.svg new file mode 100644 index 000000000..334808ae2 --- /dev/null +++ b/web/public/favicons/favicon-dark-error.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/web/public/favicons/favicon-dark-pending.png b/web/public/favicons/favicon-dark-pending.png new file mode 100644 index 000000000..02576c9bc Binary files /dev/null and b/web/public/favicons/favicon-dark-pending.png differ diff --git a/web/public/favicons/favicon-dark-pending.svg b/web/public/favicons/favicon-dark-pending.svg new file mode 100644 index 000000000..efa389fa1 --- /dev/null +++ b/web/public/favicons/favicon-dark-pending.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/web/public/favicons/favicon-dark-success.png b/web/public/favicons/favicon-dark-success.png new file mode 100644 index 000000000..2b7c9e9a4 Binary files /dev/null and b/web/public/favicons/favicon-dark-success.png differ diff --git a/web/public/favicons/favicon-dark-success.svg b/web/public/favicons/favicon-dark-success.svg new file mode 100644 index 000000000..92b8b6a1d --- /dev/null +++ b/web/public/favicons/favicon-dark-success.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/web/public/favicons/favicon-light-default.png b/web/public/favicons/favicon-light-default.png new file mode 100644 index 000000000..d55a0bf2d Binary files /dev/null and b/web/public/favicons/favicon-light-default.png differ diff --git a/web/public/favicon.svg b/web/public/favicons/favicon-light-default.svg similarity index 100% rename from web/public/favicon.svg rename to web/public/favicons/favicon-light-default.svg diff --git a/web/public/favicons/favicon-light-error.png b/web/public/favicons/favicon-light-error.png new file mode 100644 index 000000000..d7fd34752 Binary files /dev/null and b/web/public/favicons/favicon-light-error.png differ diff --git a/web/public/favicons/favicon-light-error.svg b/web/public/favicons/favicon-light-error.svg new file mode 100644 index 000000000..6bedff96e --- /dev/null +++ b/web/public/favicons/favicon-light-error.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/web/public/favicons/favicon-light-pending.png b/web/public/favicons/favicon-light-pending.png new file mode 100644 index 000000000..80fc2119b Binary files /dev/null and b/web/public/favicons/favicon-light-pending.png differ diff --git a/web/public/favicons/favicon-light-pending.svg b/web/public/favicons/favicon-light-pending.svg new file mode 100644 index 000000000..5e2b99af6 --- /dev/null +++ b/web/public/favicons/favicon-light-pending.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/web/public/favicons/favicon-light-success.png b/web/public/favicons/favicon-light-success.png new file mode 100644 index 000000000..fb0ba77b5 Binary files /dev/null and b/web/public/favicons/favicon-light-success.png differ diff --git a/web/public/favicons/favicon-light-success.svg b/web/public/favicons/favicon-light-success.svg new file mode 100644 index 000000000..e2932a2e0 --- /dev/null +++ b/web/public/favicons/favicon-light-success.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/web/src/App.vue b/web/src/App.vue index d1b49080d..960bde1fa 100644 --- a/web/src/App.vue +++ b/web/src/App.vue @@ -39,7 +39,7 @@ export default defineComponent({ const notifications = useNotifications(); // eslint-disable-next-line promise/prefer-await-to-callbacks apiClient.setErrorHandler((err) => { - notifications.notify({ title: err.message || 'An unkown error occurred', type: 'error' }); + notifications.notify({ title: err.message || 'An unknown error occurred', type: 'error' }); }); const blank = computed(() => route.meta.blank); diff --git a/web/src/compositions/useFavicon.ts b/web/src/compositions/useFavicon.ts new file mode 100644 index 000000000..e3271caba --- /dev/null +++ b/web/src/compositions/useFavicon.ts @@ -0,0 +1,54 @@ +import { computed, ref, watch } from 'vue'; + +import { useDarkMode } from '~/compositions/useDarkMode'; +import { BuildStatus } from '~/lib/api/types'; + +const darkMode = computed(() => (useDarkMode().darkMode.value ? 'dark' : 'light')); + +type Status = 'default' | 'success' | 'pending' | 'error'; +const faviconStatus = ref('default'); + +watch( + [darkMode, faviconStatus], + () => { + const faviconPNG = document.getElementById('favicon-png'); + if (faviconPNG) { + (faviconPNG as HTMLLinkElement).href = `/favicons/favicon-${darkMode.value}-${faviconStatus.value}.png`; + } + + const faviconSVG = document.getElementById('favicon-svg'); + if (faviconSVG) { + (faviconSVG as HTMLLinkElement).href = `/favicons/favicon-${darkMode.value}-${faviconStatus.value}.svg`; + } + }, + { immediate: true }, +); + +function convertStatus(status: BuildStatus): Status { + if (['blocked', 'declined', 'error', 'failure', 'killed'].includes(status)) { + return 'error'; + } + + if (['started', 'running', 'pending'].includes(status)) { + return 'pending'; + } + + if (['success', 'declined', 'error', 'failure', 'killed'].includes(status)) { + return 'success'; + } + + // skipped + return 'default'; +} + +export function useFavicon() { + return { + updateStatus(status?: BuildStatus | 'default') { + if (status === undefined || status === 'default') { + faviconStatus.value = 'default'; + } else { + faviconStatus.value = convertStatus(status); + } + }, + }; +} diff --git a/web/src/main.ts b/web/src/main.ts index 61cbb2e17..69fc06ced 100644 --- a/web/src/main.ts +++ b/web/src/main.ts @@ -1,4 +1,5 @@ import 'windi.css'; +import '~/compositions/useFavicon'; import { createPinia } from 'pinia'; import { createApp } from 'vue'; diff --git a/web/src/views/repo/RepoBuild.vue b/web/src/views/repo/RepoBuild.vue index 298f98c7d..6002b4e2f 100644 --- a/web/src/views/repo/RepoBuild.vue +++ b/web/src/views/repo/RepoBuild.vue @@ -69,7 +69,7 @@