From 848910ee8565cbf788b9a617eb6eaa53fa35d188 Mon Sep 17 00:00:00 2001 From: Damon <2184238+damongolding@users.noreply.github.com> Date: Wed, 9 Oct 2024 12:24:23 +0100 Subject: [PATCH 1/3] watching for reload and reloading config on change --- config/config.go | 54 +++++++++++++++++++++++++++------- main.go | 2 ++ routes/routes.go | 4 +-- routes/routes_cache.go | 6 ++-- routes/routes_image_helpers.go | 10 +++---- taskfile.yml | 2 +- 6 files changed, 57 insertions(+), 21 deletions(-) diff --git a/config/config.go b/config/config.go index 326adac..6358231 100644 --- a/config/config.go +++ b/config/config.go @@ -24,8 +24,11 @@ import ( "encoding/json" "errors" "strings" + "sync" + "time" "github.com/charmbracelet/log" + "github.com/fsnotify/fsnotify" "github.com/mcuadros/go-defaults" "github.com/spf13/viper" @@ -57,6 +60,9 @@ type KioskSettings struct { } type Config struct { + v *viper.Viper + mu *sync.Mutex + // ImmichApiKey Immich key to access assets ImmichApiKey string `mapstructure:"immich_api_key" default:""` // ImmichUrl Immuch base url @@ -141,7 +147,10 @@ type Config struct { // New returns a new config pointer instance func New() *Config { - c := &Config{} + c := &Config{ + v: viper.NewWithOptions(viper.ExperimentalBindStruct()), + mu: &sync.Mutex{}, + } defaults.SetDefaults(c) return c } @@ -230,29 +239,54 @@ func (c *Config) LoadWithConfigLocation(configPath string) error { return c.load(configPath) } +func (c *Config) WatchConfig() { + c.v.SetConfigFile("config.yaml") + c.v.WatchConfig() + + var debounceTimer *time.Timer + debounceTimerMutex := &sync.Mutex{} + + c.v.OnConfigChange(func(e fsnotify.Event) { + debounceTimerMutex.Lock() + defer debounceTimerMutex.Unlock() + + if debounceTimer != nil { + debounceTimer.Stop() + } + + debounceTimer = time.AfterFunc(200*time.Millisecond, func() { + log.Infof("%s changed, reloading config", e.Name) + c.mu.Lock() + defer c.mu.Unlock() + err := c.Load() + if err != nil { + log.Error("config watch", "err", err) + } + }) + }) +} + // load loads yaml config file into memory, then loads ENV vars. ENV vars overwrites yaml settings. func (c *Config) load(configFile string) error { - v := viper.NewWithOptions(viper.ExperimentalBindStruct()) - - if err := bindEnvironmentVariables(v); err != nil { + if err := bindEnvironmentVariables(c.v); err != nil { log.Errorf("binding environment variables: %v", err) } - v.AddConfigPath(".") + c.v.AddConfigPath(".") - v.SetConfigFile(configFile) + c.v.SetConfigFile(configFile) - v.SetEnvPrefix("kiosk") + c.v.SetEnvPrefix("kiosk") - v.AutomaticEnv() + c.v.AutomaticEnv() - err := v.ReadInConfig() + err := c.v.ReadInConfig() if err != nil { log.Debug("config.yaml file not being used") } - err = v.Unmarshal(&c) + err = c.v.Unmarshal(&c) if err != nil { log.Error("Environment can't be loaded", "err", err) return err diff --git a/main.go b/main.go index a53dc8d..b0f4e59 100644 --- a/main.go +++ b/main.go @@ -46,6 +46,8 @@ func main() { log.Error("Failed to load config", "err", err) } + baseConfig.WatchConfig() + if baseConfig.Kiosk.Debug { log.SetTimeFormat("15:04:05") diff --git a/routes/routes.go b/routes/routes.go index c5d79c5..4debc90 100644 --- a/routes/routes.go +++ b/routes/routes.go @@ -22,7 +22,7 @@ import ( var ( KioskVersion string - viewDataCache *cache.Cache + ViewDataCache *cache.Cache viewDataCacheMutex sync.Mutex ) @@ -38,7 +38,7 @@ type RequestData struct { func init() { // Setting up Immich api cache - viewDataCache = cache.New(5*time.Minute, 10*time.Minute) + ViewDataCache = cache.New(5*time.Minute, 10*time.Minute) } func RenderError(c echo.Context, err error, message string) error { diff --git a/routes/routes_cache.go b/routes/routes_cache.go index 2fa0348..a1246f7 100644 --- a/routes/routes_cache.go +++ b/routes/routes_cache.go @@ -13,12 +13,12 @@ func FlushCache(c echo.Context) error { viewDataCacheMutex.Lock() defer viewDataCacheMutex.Unlock() - log.Info("Cache before flush", "viewDataCache_items", viewDataCache.ItemCount(), "apiCache_items", immich.ApiCacheCount()) + log.Info("Cache before flush", "viewDataCache_items", ViewDataCache.ItemCount(), "apiCache_items", immich.ApiCacheCount()) - viewDataCache.Flush() + ViewDataCache.Flush() immich.FluchApiCache() - log.Info("Cache after flush ", "viewDataCache_items", viewDataCache.ItemCount(), "apiCache_items", immich.ApiCacheCount()) + log.Info("Cache after flush ", "viewDataCache_items", ViewDataCache.ItemCount(), "apiCache_items", immich.ApiCacheCount()) c.Response().Header().Set("HX-Refresh", "true") return c.NoContent(http.StatusOK) diff --git a/routes/routes_image_helpers.go b/routes/routes_image_helpers.go index d5d6dab..b918cff 100644 --- a/routes/routes_image_helpers.go +++ b/routes/routes_image_helpers.go @@ -252,13 +252,13 @@ func imagePreFetch(requestConfig config.Config, c echo.Context, kioskDeviceID st cacheKey := c.Request().URL.String() + kioskDeviceID - if data, found := viewDataCache.Get(cacheKey); found { + if data, found := ViewDataCache.Get(cacheKey); found { cachedViewData = data.([]views.ViewData) } cachedViewData = append(cachedViewData, viewDataToAdd) - viewDataCache.Set(cacheKey, cachedViewData, cache.DefaultExpiration) + ViewDataCache.Set(cacheKey, cachedViewData, cache.DefaultExpiration) } @@ -309,12 +309,12 @@ func fromCache(c echo.Context, kioskDeviceID string) []views.ViewData { defer viewDataCacheMutex.Unlock() cacheKey := c.Request().URL.String() + kioskDeviceID - if data, found := viewDataCache.Get(cacheKey); found { + if data, found := ViewDataCache.Get(cacheKey); found { cachedPageData := data.([]views.ViewData) if len(cachedPageData) > 0 { return cachedPageData } - viewDataCache.Delete(cacheKey) + ViewDataCache.Delete(cacheKey) } return nil } @@ -329,7 +329,7 @@ func renderCachedViewData(c echo.Context, cachedViewData []views.ViewData, reque cacheKey := c.Request().URL.String() + kioskDeviceID viewDataToRender := cachedViewData[0] - viewDataCache.Set(cacheKey, cachedViewData[1:], cache.DefaultExpiration) + ViewDataCache.Set(cacheKey, cachedViewData[1:], cache.DefaultExpiration) // Update history which will be outdated in cache trimHistory(&requestConfig.History, 10) diff --git a/taskfile.yml b/taskfile.yml index 5221a7e..d5869dd 100644 --- a/taskfile.yml +++ b/taskfile.yml @@ -1,6 +1,6 @@ version: "3" env: - VERSION: 0.11.1 + VERSION: 0.11.2 tasks: default: deps: [build] From 3e66cb355a26e0219d598a5027d9f89cb362e814 Mon Sep 17 00:00:00 2001 From: Damon <2184238+damongolding@users.noreply.github.com> Date: Wed, 9 Oct 2024 12:30:47 +0100 Subject: [PATCH 2/3] fix broken test --- config/config_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/config_test.go b/config/config_test.go index 763c903..739ad51 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -85,7 +85,7 @@ func TestMalformedURLs(t *testing.T) { t.Setenv("KIOSK_IMMICH_URL", test.KIOSK_IMMICH_URL) t.Setenv("KIOSK_IMMICH_API_KEY", "12345") - var c Config + c := New() err := c.Load() assert.NoError(t, err, "Config load should not return an error") From 00d932731fcd844f7f4845b867078a7df62d94c0 Mon Sep 17 00:00:00 2001 From: Damon <2184238+damongolding@users.noreply.github.com> Date: Wed, 9 Oct 2024 16:15:26 +0100 Subject: [PATCH 3/3] check if config file has changed --- Dockerfile | 1 - config/config.go | 97 ++++++++++++++++++++++------- main.go | 2 + routes/routes_clock.go | 7 --- routes/routes_refresh.go | 38 ++++++++++++ taskfile.yml | 4 +- views/views_home.templ | 14 ++++- views/views_home_templ.go | 127 ++++++++++++++++++++++++-------------- 8 files changed, 206 insertions(+), 84 deletions(-) create mode 100644 routes/routes_refresh.go diff --git a/Dockerfile b/Dockerfile index 83abfa9..ed48870 100644 --- a/Dockerfile +++ b/Dockerfile @@ -7,7 +7,6 @@ ARG TARGETARCH WORKDIR /app COPY . . -COPY config.example.yaml /app/config/ RUN go mod download RUN CGO_ENABLED=0 GOOS=$TARGETOS GOARCH=$TARGETARCH go build -ldflags "-X main.version=${VERSION}" -o dist/kiosk . diff --git a/config/config.go b/config/config.go index 6358231..7feafc3 100644 --- a/config/config.go +++ b/config/config.go @@ -23,12 +23,12 @@ package config import ( "encoding/json" "errors" + "os" "strings" "sync" "time" "github.com/charmbracelet/log" - "github.com/fsnotify/fsnotify" "github.com/mcuadros/go-defaults" "github.com/spf13/viper" @@ -39,6 +39,7 @@ const ( defaultImmichPort = "2283" defaultScheme = "http://" DefaultDateLayout = "02/01/2006" + defaultConfigFile = "config.yaml" ) type KioskSettings struct { @@ -60,8 +61,14 @@ type KioskSettings struct { } type Config struct { - v *viper.Viper + // v is the viper instance used for configuration management + v *viper.Viper + // mu is a mutex used to ensure thread-safe access to the configuration mu *sync.Mutex + // ReloadTimeStamp timestamp for when the last client reload was called for + ReloadTimeStamp string + // configLastModTime stores the last modification time of the configuration file + configLastModTime time.Time // ImmichApiKey Immich key to access assets ImmichApiKey string `mapstructure:"immich_api_key" default:""` @@ -148,13 +155,29 @@ type Config struct { // New returns a new config pointer instance func New() *Config { c := &Config{ - v: viper.NewWithOptions(viper.ExperimentalBindStruct()), - mu: &sync.Mutex{}, + v: viper.NewWithOptions(viper.ExperimentalBindStruct()), + mu: &sync.Mutex{}, + ReloadTimeStamp: time.Now().Format(time.RFC3339), } defaults.SetDefaults(c) + info, err := os.Stat(defaultConfigFile) + if err == nil { + c.configLastModTime = info.ModTime() + } return c } +// hasConfigChanged checks if the configuration file has been modified since the last check. +func (c *Config) hasConfigChanged() bool { + info, err := os.Stat(defaultConfigFile) + if err != nil { + log.Errorf("Checking config file: %v", err) + return false + } + + return info.ModTime().After(c.configLastModTime) +} + // bindEnvironmentVariables binds specific environment variables to their corresponding // configuration keys in the Viper instance. This function allows for easy mapping // between environment variables and configuration settings. @@ -231,7 +254,7 @@ func (c *Config) checkDebuging() { // Load loads yaml config file into memory, then loads ENV vars. ENV vars overwrites yaml settings. func (c *Config) Load() error { - return c.load("config.yaml") + return c.load(defaultConfigFile) } // Load loads yaml config file into memory with a custom path, then loads ENV vars. ENV vars overwrites yaml settings. @@ -239,31 +262,57 @@ func (c *Config) LoadWithConfigLocation(configPath string) error { return c.load(configPath) } +// WatchConfig starts a goroutine that periodically checks for changes in the configuration file +// and reloads the configuration if changes are detected. +// +// This function performs the following actions: +// 1. Retrieves the initial modification time of the config file. +// 2. Starts a goroutine that runs indefinitely. +// 3. Uses a ticker to check for config changes every 5 seconds. +// 4. If changes are detected, it reloads the configuration and updates the ReloadTimeStamp. func (c *Config) WatchConfig() { - c.v.SetConfigFile("config.yaml") - c.v.WatchConfig() - var debounceTimer *time.Timer - debounceTimerMutex := &sync.Mutex{} + fileInfo, err := os.Stat(defaultConfigFile) + if os.IsNotExist(err) { + return + } - c.v.OnConfigChange(func(e fsnotify.Event) { - debounceTimerMutex.Lock() - defer debounceTimerMutex.Unlock() + if fileInfo.IsDir() { + log.Errorf("Config file %s is a directory", defaultConfigFile) + return + } - if debounceTimer != nil { - debounceTimer.Stop() - } + info, err := os.Stat(defaultConfigFile) + if err != nil { + log.Infof("Error getting initial file info: %v", err) + } else { + c.configLastModTime = info.ModTime() + } - debounceTimer = time.AfterFunc(200*time.Millisecond, func() { - log.Infof("%s changed, reloading config", e.Name) - c.mu.Lock() - defer c.mu.Unlock() - err := c.Load() - if err != nil { - log.Error("config watch", "err", err) + go func() { + ticker := time.NewTicker(5 * time.Second) + defer ticker.Stop() + + //nolint:gosimple // Using for-select for ticker and potential future cases + for { + select { + case <-ticker.C: + if c.hasConfigChanged() { + log.Info("Config file changed, reloading config") + c.mu.Lock() + err := c.Load() + if err != nil { + log.Errorf("Reloading config: %v", err) + } else { + c.ReloadTimeStamp = time.Now().Format(time.RFC3339) + info, _ := os.Stat(defaultConfigFile) + c.configLastModTime = info.ModTime() + } + c.mu.Unlock() + } } - }) - }) + } + }() } // load loads yaml config file into memory, then loads ENV vars. ENV vars overwrites yaml settings. diff --git a/main.go b/main.go index b0f4e59..fa40e69 100644 --- a/main.go +++ b/main.go @@ -109,6 +109,8 @@ func main() { e.GET("/cache/flush", routes.FlushCache) + e.POST("/refresh/check", routes.RefreshCheck(baseConfig)) + err = e.Start(":3000") if err != nil { log.Fatal(err) diff --git a/routes/routes_clock.go b/routes/routes_clock.go index ee76e8e..be24ccd 100644 --- a/routes/routes_clock.go +++ b/routes/routes_clock.go @@ -15,18 +15,11 @@ import ( func Clock(baseConfig *config.Config) echo.HandlerFunc { return func(c echo.Context) error { - kioskVersionHeader := c.Request().Header.Get("kiosk-version") requestID := utils.ColorizeRequestId(c.Response().Header().Get(echo.HeaderXRequestID)) // create a copy of the global config to use with this request requestConfig := *baseConfig - // If kiosk version on client and server do not match refresh client. - if kioskVersionHeader != "" && KioskVersion != kioskVersionHeader { - c.Response().Header().Set("HX-Refresh", "true") - return c.NoContent(http.StatusOK) - } - err := requestConfig.ConfigWithOverrides(c) if err != nil { log.Error("overriding config", "err", err) diff --git a/routes/routes_refresh.go b/routes/routes_refresh.go new file mode 100644 index 0000000..2be178c --- /dev/null +++ b/routes/routes_refresh.go @@ -0,0 +1,38 @@ +package routes + +import ( + "net/http" + + "github.com/charmbracelet/log" + "github.com/labstack/echo/v4" + + "github.com/damongolding/immich-kiosk/config" + "github.com/damongolding/immich-kiosk/utils" +) + +// RefreshCheck endpoint to check if device requires a refresh +func RefreshCheck(baseConfig *config.Config) echo.HandlerFunc { + return func(c echo.Context) error { + + kioskVersionHeader := c.Request().Header.Get("kiosk-version") + kioskRefreshTimestampHeader := c.Request().Header.Get("kiosk-reload-timestamp") + requestID := utils.ColorizeRequestId(c.Response().Header().Get(echo.HeaderXRequestID)) + + // create a copy of the global config to use with this request + requestConfig := *baseConfig + + // If kiosk version on client and server do not match refresh client. + if KioskVersion != kioskVersionHeader || kioskRefreshTimestampHeader != requestConfig.ReloadTimeStamp { + c.Response().Header().Set("HX-Refresh", "true") + return c.NoContent(http.StatusOK) + } + + log.Debug( + requestID, + "method", c.Request().Method, + "path", c.Request().URL.String(), + ) + + return c.NoContent(http.StatusOK) + } +} diff --git a/taskfile.yml b/taskfile.yml index d5869dd..91943e7 100644 --- a/taskfile.yml +++ b/taskfile.yml @@ -1,6 +1,6 @@ version: "3" env: - VERSION: 0.11.2 + VERSION: 0.11.2-beta.2 tasks: default: deps: [build] @@ -53,7 +53,7 @@ tasks: docker-image: cmds: - - docker build --build-arg VERSION={{.VERSION}} --load -t damongolding/immich-kiosk:{{.VERSION}} -t damongolding/immich-kiosk:latest . + - docker build --no-cache --build-arg VERSION={{.VERSION}} --load -t damongolding/immich-kiosk:{{.VERSION}} -t damongolding/immich-kiosk:latest . docker-buildx: cmds: diff --git a/views/views_home.templ b/views/views_home.templ index 996ee35..0476745 100644 --- a/views/views_home.templ +++ b/views/views_home.templ @@ -201,7 +201,7 @@ templ paramForm(queries url.Values) { } -templ clock(queries url.Values, kioskVersion string, deviceID string, theme string) { +templ clock(queries url.Values, theme string) {
} @@ -236,6 +235,14 @@ templ sleepMode(sleepStart, sleepEnd string, queries url.Values) { } } +templ refreshCheckForm(kioskVersion, reloadTimeStamp string) { +
+} + templ Home(viewData ViewData) { @@ -289,12 +296,13 @@ templ Home(viewData ViewData) { @progressBar() } if !viewData.DisableUi && (viewData.ShowTime || viewData.ShowDate) { - @clock(viewData.Queries, viewData.KioskVersion, viewData.DeviceID, viewData.Theme) + @clock(viewData.Queries, viewData.Theme) } @menu() @paramForm(viewData.Queries) @sleepMode(viewData.SleepStart, viewData.SleepEnd, viewData.Queries) @historyForm() + @refreshCheckForm(viewData.KioskVersion, viewData.ReloadTimeStamp) @offlineIcon() @kioskData(map[string]any{ "debug": viewData.Kiosk.Debug, diff --git a/views/views_home_templ.go b/views/views_home_templ.go index 7221982..2e4e0fb 100644 --- a/views/views_home_templ.go +++ b/views/views_home_templ.go @@ -523,7 +523,7 @@ func paramForm(queries url.Values) templ.Component { }) } -func clock(queries url.Values, kioskVersion string, deviceID string, theme string) templ.Component { +func clock(queries url.Values, theme string) templ.Component { return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { @@ -572,20 +572,7 @@ func clock(queries url.Values, kioskVersion string, deviceID string, theme strin return templ_7745c5c3_Err } } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" hx-trigger=\"load, every 13s\" hx-swap=\"innerHTML\" hx-headers=\"") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - var templ_7745c5c3_Var19 string - templ_7745c5c3_Var19, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf(`{"kiosk-version": "%s", "kiosk-device-id": "%s"}`, kioskVersion, deviceID)) - if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/views_home.templ`, Line: 214, Col: 102} - } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var19)) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\">") + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" hx-trigger=\"load, every 13s\" hx-swap=\"innerHTML\">") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -609,9 +596,9 @@ func progressBar() templ.Component { }() } ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var20 := templ.GetChildren(ctx) - if templ_7745c5c3_Var20 == nil { - templ_7745c5c3_Var20 = templ.NopComponent + templ_7745c5c3_Var19 := templ.GetChildren(ctx) + if templ_7745c5c3_Var19 == nil { + templ_7745c5c3_Var19 = templ.NopComponent } ctx = templ.ClearChildren(ctx) _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
") @@ -638,9 +625,9 @@ func sleepMode(sleepStart, sleepEnd string, queries url.Values) templ.Component }() } ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var21 := templ.GetChildren(ctx) - if templ_7745c5c3_Var21 == nil { - templ_7745c5c3_Var21 = templ.NopComponent + templ_7745c5c3_Var20 := templ.GetChildren(ctx) + if templ_7745c5c3_Var20 == nil { + templ_7745c5c3_Var20 = templ.NopComponent } ctx = templ.ClearChildren(ctx) if sleepStart != "" && sleepEnd != "" { @@ -663,6 +650,48 @@ func sleepMode(sleepStart, sleepEnd string, queries url.Values) templ.Component }) } +func refreshCheckForm(kioskVersion, reloadTimeStamp string) templ.Component { + return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { + templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context + if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { + return templ_7745c5c3_CtxErr + } + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) + if !templ_7745c5c3_IsBuffer { + defer func() { + templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) + if templ_7745c5c3_Err == nil { + templ_7745c5c3_Err = templ_7745c5c3_BufErr + } + }() + } + ctx = templ.InitializeContext(ctx) + templ_7745c5c3_Var21 := templ.GetChildren(ctx) + if templ_7745c5c3_Var21 == nil { + templ_7745c5c3_Var21 = templ.NopComponent + } + ctx = templ.ClearChildren(ctx) + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return templ_7745c5c3_Err + }) +} + func Home(viewData ViewData) templ.Component { return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context @@ -679,17 +708,17 @@ func Home(viewData ViewData) templ.Component { }() } ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var22 := templ.GetChildren(ctx) - if templ_7745c5c3_Var22 == nil { - templ_7745c5c3_Var22 = templ.NopComponent + templ_7745c5c3_Var23 := templ.GetChildren(ctx) + if templ_7745c5c3_Var23 == nil { + templ_7745c5c3_Var23 = templ.NopComponent } ctx = templ.ClearChildren(ctx) _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - var templ_7745c5c3_Var23 = []any{baseFontSize(viewData.FontSize)} - templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var23...) + var templ_7745c5c3_Var24 = []any{baseFontSize(viewData.FontSize)} + templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var24...) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -697,12 +726,12 @@ func Home(viewData ViewData) templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - var templ_7745c5c3_Var24 string - templ_7745c5c3_Var24, templ_7745c5c3_Err = templ.JoinStringErrs(templ.CSSClasses(templ_7745c5c3_Var23).String()) + var templ_7745c5c3_Var25 string + templ_7745c5c3_Var25, templ_7745c5c3_Err = templ.JoinStringErrs(templ.CSSClasses(templ_7745c5c3_Var24).String()) if templ_7745c5c3_Err != nil { return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/views_home.templ`, Line: 1, Col: 0} } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var24)) + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var25)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -710,12 +739,12 @@ func Home(viewData ViewData) templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - var templ_7745c5c3_Var25 string - templ_7745c5c3_Var25, templ_7745c5c3_Err = templ.JoinStringErrs(viewData.KioskVersion) + var templ_7745c5c3_Var26 string + templ_7745c5c3_Var26, templ_7745c5c3_Err = templ.JoinStringErrs(viewData.KioskVersion) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/views_home.templ`, Line: 246, Col: 55} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/views_home.templ`, Line: 253, Col: 55} } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var25)) + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var26)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -723,12 +752,12 @@ func Home(viewData ViewData) templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - var templ_7745c5c3_Var26 string - templ_7745c5c3_Var26, templ_7745c5c3_Err = templ.JoinStringErrs(string(templ.URL(fmt.Sprintf("/assets/css/kiosk.%s.css", viewData.KioskVersion)))) + var templ_7745c5c3_Var27 string + templ_7745c5c3_Var27, templ_7745c5c3_Err = templ.JoinStringErrs(string(templ.URL(fmt.Sprintf("/assets/css/kiosk.%s.css", viewData.KioskVersion)))) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/views_home.templ`, Line: 248, Col: 114} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/views_home.templ`, Line: 255, Col: 114} } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var26)) + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var27)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -760,8 +789,8 @@ func Home(viewData ViewData) templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - var templ_7745c5c3_Var27 = []any{templ.KV("layout-splitview", strings.EqualFold(viewData.Layout, "splitview"))} - templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var27...) + var templ_7745c5c3_Var28 = []any{templ.KV("layout-splitview", strings.EqualFold(viewData.Layout, "splitview"))} + templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var28...) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -769,12 +798,12 @@ func Home(viewData ViewData) templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - var templ_7745c5c3_Var28 string - templ_7745c5c3_Var28, templ_7745c5c3_Err = templ.JoinStringErrs(templ.CSSClasses(templ_7745c5c3_Var27).String()) + var templ_7745c5c3_Var29 string + templ_7745c5c3_Var29, templ_7745c5c3_Err = templ.JoinStringErrs(templ.CSSClasses(templ_7745c5c3_Var28).String()) if templ_7745c5c3_Err != nil { return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/views_home.templ`, Line: 1, Col: 0} } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var28)) + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var29)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -806,7 +835,7 @@ func Home(viewData ViewData) templ.Component { } } if !viewData.DisableUi && (viewData.ShowTime || viewData.ShowDate) { - templ_7745c5c3_Err = clock(viewData.Queries, viewData.KioskVersion, viewData.DeviceID, viewData.Theme).Render(ctx, templ_7745c5c3_Buffer) + templ_7745c5c3_Err = clock(viewData.Queries, viewData.Theme).Render(ctx, templ_7745c5c3_Buffer) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -827,6 +856,10 @@ func Home(viewData ViewData) templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } + templ_7745c5c3_Err = refreshCheckForm(viewData.KioskVersion, viewData.ReloadTimeStamp).Render(ctx, templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } templ_7745c5c3_Err = offlineIcon().Render(ctx, templ_7745c5c3_Buffer) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err @@ -846,12 +879,12 @@ func Home(viewData ViewData) templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - var templ_7745c5c3_Var29 string - templ_7745c5c3_Var29, templ_7745c5c3_Err = templ.JoinStringErrs(string(templ.URL(fmt.Sprintf("/assets/js/kiosk.%s.js", viewData.KioskVersion)))) + var templ_7745c5c3_Var30 string + templ_7745c5c3_Var30, templ_7745c5c3_Err = templ.JoinStringErrs(string(templ.URL(fmt.Sprintf("/assets/js/kiosk.%s.js", viewData.KioskVersion)))) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/views_home.templ`, Line: 307, Col: 96} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/views_home.templ`, Line: 315, Col: 96} } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var29)) + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var30)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err }