htmgo/framework/h/app.go

152 lines
3 KiB
Go
Raw Normal View History

2024-09-11 00:52:18 +00:00
package h
import (
"fmt"
2024-09-17 17:13:22 +00:00
"github.com/labstack/echo/v4"
"github.com/maddalax/htmgo/framework/htmgo/service"
2024-09-21 16:52:56 +00:00
"github.com/maddalax/htmgo/framework/hx"
"github.com/maddalax/htmgo/framework/util/process"
"log/slog"
"time"
2024-09-11 00:52:18 +00:00
)
type RequestContext struct {
echo.Context
2024-09-21 16:52:56 +00:00
locator *service.Locator
isBoosted bool
currentBrowserUrl string
hxPromptResponse string
isHxRequest bool
hxTargetId string
hxTriggerName string
hxTriggerId string
}
func (c *RequestContext) ServiceLocator() *service.Locator {
return c.locator
}
type AppOpts struct {
LiveReload bool
ServiceLocator *service.Locator
Register func(echo *echo.Echo)
}
2024-09-11 00:52:18 +00:00
type App struct {
Opts AppOpts
Echo *echo.Echo
2024-09-11 00:52:18 +00:00
}
var instance *App
func GetApp() *App {
if instance == nil {
panic("App instance not initialized")
}
return instance
}
func Start(opts AppOpts) {
e := echo.New()
instance := App{
Opts: opts,
Echo: e,
}
instance.start()
2024-09-11 00:52:18 +00:00
}
2024-09-21 16:52:56 +00:00
func populateHxFields(cc *RequestContext) {
cc.isBoosted = cc.Request().Header.Get(hx.BoostedHeader) == "true"
cc.currentBrowserUrl = cc.Request().Header.Get(hx.CurrentUrlHeader)
cc.hxPromptResponse = cc.Request().Header.Get(hx.PromptResponseHeader)
cc.isHxRequest = cc.Request().Header.Get(hx.RequestHeader) == "true"
cc.hxTargetId = cc.Request().Header.Get(hx.TargetIdHeader)
cc.hxTriggerName = cc.Request().Header.Get(hx.TriggerNameHeader)
cc.hxTriggerId = cc.Request().Header.Get(hx.TriggerIdHeader)
}
func (a App) start() {
2024-09-11 00:52:18 +00:00
2024-09-20 18:25:14 +00:00
if a.Opts.Register != nil {
a.Opts.Register(a.Echo)
}
a.Echo.Use(func(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
cc := &RequestContext{
2024-09-21 16:52:56 +00:00
Context: c,
locator: a.Opts.ServiceLocator,
}
2024-09-21 16:52:56 +00:00
populateHxFields(cc)
return next(cc)
}
})
if a.Opts.LiveReload {
2024-09-17 17:13:22 +00:00
AddLiveReloadHandler("/dev/livereload", a.Echo)
2024-09-11 00:52:18 +00:00
}
port := ":3000"
2024-09-17 17:13:22 +00:00
err := a.Echo.Start(port)
2024-09-11 00:52:18 +00:00
if err != nil {
// If we are in watch mode, just try to kill any processes holding that port
// and try again
if IsDevelopment() && IsWatchMode() {
slog.Info("Port already in use, trying to kill the process and start again")
process.RunOrExit(fmt.Sprintf("kill -9 $(lsof -t -i%s)", port))
time.Sleep(time.Millisecond * 50)
2024-09-17 17:13:22 +00:00
err = a.Echo.Start(port)
if err != nil {
panic(err)
}
} else {
panic(err)
}
2024-09-11 00:52:18 +00:00
}
}
2024-09-17 17:13:22 +00:00
func HtmlView(c echo.Context, page *Page) error {
root := page.Root
2024-09-17 17:13:22 +00:00
return c.HTML(200,
2024-09-11 00:52:18 +00:00
Render(
root,
),
)
}
2024-09-17 17:13:22 +00:00
func PartialViewWithHeaders(c echo.Context, headers *Headers, partial *Partial) error {
2024-09-11 18:09:55 +00:00
if partial.Headers != nil {
for s, a := range *partial.Headers {
c.Set(s, a)
}
}
if headers != nil {
for s, a := range *headers {
2024-09-19 23:11:12 +00:00
c.Response().Header().Set(s, a)
2024-09-11 18:09:55 +00:00
}
}
2024-09-17 17:13:22 +00:00
return c.HTML(200,
2024-09-11 18:09:55 +00:00
Render(
partial,
2024-09-11 18:09:55 +00:00
),
)
}
2024-09-17 17:13:22 +00:00
func PartialView(c echo.Context, partial *Partial) error {
c.Set(echo.HeaderContentType, echo.MIMETextHTML)
2024-09-11 00:52:18 +00:00
if partial.Headers != nil {
for s, a := range *partial.Headers {
2024-09-19 23:11:12 +00:00
c.Response().Header().Set(s, a)
2024-09-11 00:52:18 +00:00
}
}
2024-09-17 17:13:22 +00:00
return c.HTML(200,
2024-09-11 00:52:18 +00:00
Render(
partial,
2024-09-11 00:52:18 +00:00
),
)
}