Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
03b6d7453a |
31
context.go
31
context.go
@@ -318,6 +318,37 @@ func (c *Context) ExecScript(s string) {
|
||||
c.sendPatch(patch{patchTypeScript, s})
|
||||
}
|
||||
|
||||
// Redirect navigates the browser to the given URL.
|
||||
// This triggers a full page navigation - the current context will be disposed
|
||||
// and a new context created at the destination URL.
|
||||
func (c *Context) Redirect(url string) {
|
||||
if url == "" {
|
||||
c.app.logWarn(c, "redirect failed: empty url")
|
||||
return
|
||||
}
|
||||
c.sendPatch(patch{patchTypeRedirect, url})
|
||||
}
|
||||
|
||||
// Redirectf navigates the browser to a URL constructed from the format string and arguments.
|
||||
func (c *Context) Redirectf(format string, a ...any) {
|
||||
c.Redirect(fmt.Sprintf(format, a...))
|
||||
}
|
||||
|
||||
// ReplaceURL updates the browser's URL and history without triggering navigation.
|
||||
// Useful for updating query params or path to reflect UI state changes.
|
||||
func (c *Context) ReplaceURL(url string) {
|
||||
if url == "" {
|
||||
c.app.logWarn(c, "replace url failed: empty url")
|
||||
return
|
||||
}
|
||||
c.sendPatch(patch{patchTypeReplaceURL, url})
|
||||
}
|
||||
|
||||
// ReplaceURLf updates the browser's URL using a format string.
|
||||
func (c *Context) ReplaceURLf(format string, a ...any) {
|
||||
c.ReplaceURL(fmt.Sprintf(format, a...))
|
||||
}
|
||||
|
||||
// stopAllRoutines stops all go routines tied to this Context preventing goroutine leaks.
|
||||
func (c *Context) stopAllRoutines() {
|
||||
select {
|
||||
|
||||
@@ -7,11 +7,11 @@ import (
|
||||
|
||||
func main() {
|
||||
v := via.New()
|
||||
v.Config(via.Options{ServerAddress: ":7331"})
|
||||
|
||||
v.Page("/", func(c *via.Context) {
|
||||
username := c.Session().GetString("username")
|
||||
// Login page
|
||||
v.Page("/login", func(c *via.Context) {
|
||||
flash := c.Session().PopString("flash")
|
||||
|
||||
usernameInput := c.Signal("")
|
||||
|
||||
login := c.Action(func() {
|
||||
@@ -20,38 +20,69 @@ func main() {
|
||||
c.Session().Set("username", name)
|
||||
c.Session().Set("flash", "Welcome, "+name+"!")
|
||||
c.Session().RenewToken()
|
||||
c.Redirect("/dashboard")
|
||||
}
|
||||
c.Sync()
|
||||
})
|
||||
|
||||
logout := c.Action(func() {
|
||||
c.Session().Set("flash", "Goodbye!")
|
||||
c.Session().Delete("username")
|
||||
c.Sync()
|
||||
})
|
||||
|
||||
c.View(func() h.H {
|
||||
// Already logged in? Redirect to dashboard
|
||||
if c.Session().GetString("username") != "" {
|
||||
c.Redirect("/dashboard")
|
||||
return h.Div()
|
||||
}
|
||||
|
||||
var flashMsg h.H
|
||||
if flash != "" {
|
||||
flashMsg = h.P(h.Text(flash), h.Style("color: green"))
|
||||
}
|
||||
|
||||
if username == "" {
|
||||
return h.Div(
|
||||
flashMsg,
|
||||
h.H1(h.Text("Login")),
|
||||
h.Input(h.Type("text"), h.Placeholder("Username"), usernameInput.Bind()),
|
||||
h.Button(h.Text("Login"), login.OnClick()),
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
// Dashboard page (protected)
|
||||
v.Page("/dashboard", func(c *via.Context) {
|
||||
logout := c.Action(func() {
|
||||
c.Session().Set("flash", "Goodbye!")
|
||||
c.Session().Delete("username")
|
||||
c.Redirect("/login")
|
||||
})
|
||||
|
||||
c.View(func() h.H {
|
||||
username := c.Session().GetString("username")
|
||||
|
||||
// Not logged in? Redirect to login
|
||||
if username == "" {
|
||||
c.Session().Set("flash", "Please log in first")
|
||||
c.Redirect("/login")
|
||||
return h.Div()
|
||||
}
|
||||
|
||||
flash := c.Session().PopString("flash")
|
||||
var flashMsg h.H
|
||||
if flash != "" {
|
||||
flashMsg = h.P(h.Text(flash), h.Style("color: green"))
|
||||
}
|
||||
return h.Div(
|
||||
flashMsg,
|
||||
h.H1(h.Textf("Hello, %s!", username)),
|
||||
h.H1(h.Textf("Dashboard - Hello, %s!", username)),
|
||||
h.P(h.Text("Your session persists across page refreshes.")),
|
||||
h.Button(h.Text("Logout"), logout.OnClick()),
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
// Redirect root to login
|
||||
v.Page("/", func(c *via.Context) {
|
||||
c.View(func() h.H {
|
||||
c.Redirect("/login")
|
||||
return h.Div()
|
||||
})
|
||||
})
|
||||
|
||||
v.Start()
|
||||
}
|
||||
|
||||
18
via.go
18
via.go
@@ -15,6 +15,7 @@ import (
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
@@ -359,6 +360,8 @@ const (
|
||||
patchTypeElements = iota
|
||||
patchTypeSignals
|
||||
patchTypeScript
|
||||
patchTypeRedirect
|
||||
patchTypeReplaceURL
|
||||
)
|
||||
|
||||
type patch struct {
|
||||
@@ -453,6 +456,21 @@ func New() *V {
|
||||
v.logErr(c, "ExecuteScript failed: %v", err)
|
||||
}
|
||||
}
|
||||
case patchTypeRedirect:
|
||||
if err := sse.Redirect(patch.content); err != nil {
|
||||
if sse.Context().Err() == nil {
|
||||
v.logErr(c, "Redirect failed: %v", err)
|
||||
}
|
||||
}
|
||||
case patchTypeReplaceURL:
|
||||
parsedURL, err := url.Parse(patch.content)
|
||||
if err != nil {
|
||||
v.logErr(c, "ReplaceURL failed to parse URL: %v", err)
|
||||
} else if err := sse.ReplaceURL(*parsedURL); err != nil {
|
||||
if sse.Context().Err() == nil {
|
||||
v.logErr(c, "ReplaceURL failed: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user