- {{ include "components/artwork" Illust }}
+ {{ include "components/artwork" .Illust }}
+{{end}}
diff --git a/assets/layout/userAtom.jet.html b/assets/views/userAtom.jet.html
similarity index 96%
rename from assets/layout/userAtom.jet.html
rename to assets/views/userAtom.jet.html
index dbc9d6f..6061299 100644
--- a/assets/layout/userAtom.jet.html
+++ b/assets/views/userAtom.jet.html
@@ -1,3 +1,5 @@
+{{extends "layout/default.jet"}}
+{{block body()}}
{* *}
{{ BaseURL }}/users/{{ User.ID }}/{{ Category }}
@@ -40,4 +42,4 @@
{{ end }}
-
\ No newline at end of file
+{{end}}
diff --git a/core/novel.go b/core/novel.go
index 9160bc2..d9e546b 100644
--- a/core/novel.go
+++ b/core/novel.go
@@ -31,10 +31,10 @@ type Novel struct {
Content string `json:"content"`
CoverURL string `json:"coverUrl"`
IsBookmarkable bool `json:"isBookmarkable"`
- BookmarkData interface{} `json:"bookmarkData"`
+ BookmarkData any `json:"bookmarkData"`
LikeData bool `json:"likeData"`
- PollData interface{} `json:"pollData"`
- Marker interface{} `json:"marker"`
+ PollData any `json:"pollData"`
+ Marker any `json:"marker"`
Tags struct {
AuthorID string `json:"authorId"`
IsLocked bool `json:"isLocked"`
@@ -43,7 +43,7 @@ type Novel struct {
} `json:"tags"`
Writable bool `json:"writable"`
} `json:"tags"`
- SeriesNavData interface{} `json:"seriesNavData"`
+ SeriesNavData any `json:"seriesNavData"`
HasGlossary bool `json:"hasGlossary"`
IsUnlisted bool `json:"isUnlisted"`
// seen values: zh-cn, ja
@@ -76,7 +76,7 @@ type NovelBrief struct {
ReadingTime int `json:"readingTime"`
Description string `json:"description"`
IsBookmarkable bool `json:"isBookmarkable"`
- BookmarkData interface{} `json:"bookmarkData"`
+ BookmarkData any `json:"bookmarkData"`
Bookmarks int `json:"bookmarkCount"`
IsOriginal bool `json:"isOriginal"`
CreateDate time.Time `json:"createDate"`
diff --git a/core/user.go b/core/user.go
index 0b2ef3d..6294e9e 100644
--- a/core/user.go
+++ b/core/user.go
@@ -53,7 +53,7 @@ type User struct {
SocialRaw json.RawMessage `json:"social"`
Artworks []ArtworkBrief `json:"artworks"`
Novels []NovelBrief `json:"novels"`
- Background map[string]interface{} `json:"background"`
+ Background map[string]any `json:"background"`
ArtworksCount int
FrequentTags []FrequentTag
Social map[string]map[string]string
diff --git a/doc/dev/framework-migration.md b/doc/dev/framework-migration.md
index a47b7fe..b87a8cf 100644
--- a/doc/dev/framework-migration.md
+++ b/doc/dev/framework-migration.md
@@ -1,6 +1,14 @@
# Migrating from gofiber to net/http -- the plan
-Templating
- First, convert all `c.Render("about", ...)` to `Render(c, Data_about{...}`.
- Then, the templating engine is decoupled from gofiber.
+## Templating
+First, convert all `c.Render("about", ...)` to `Render(c, Data_about{...}`.
+Then, the templating engine is decoupled from gofiber.
+
+### Fixing current templates
+
+gofiber doesn't mention this crucial distinction of Jet: the difference between variables (e.g. PageURL) and data (the data parameter to `Render(...)`).
+
+Data access must be prefixed with a dot. `.Illust` is valid. `Illust` is a variable but not data.
+
+Current templates use the variable style (`Illust`), but that is wrong.
diff --git a/main.go b/main.go
index f03c8de..f299bbd 100644
--- a/main.go
+++ b/main.go
@@ -50,7 +50,9 @@ func main() {
config.GlobalServerConfig.InitializeConfig()
core.CreateResponseAuditFolder()
- engine := jet.New("./assets/layout", ".jet.html")
+ routes.InitTemplatingEngine(config.GlobalServerConfig.InDevelopment)
+ // the code below is redundant
+ engine := jet.New("./assets/views", ".jet.html")
engine.AddFuncMap(utils.GetTemplateFunctions())
if config.GlobalServerConfig.InDevelopment {
engine.Reload(true)
@@ -60,6 +62,7 @@ func main() {
if err != nil {
panic(err)
}
+ // the code above is redundant
server := fiber.New(fiber.Config{
AppName: "PixivFE",
diff --git a/routes/pixivision.go b/routes/pixivision.go
index 6c7072e..6cd9eb7 100644
--- a/routes/pixivision.go
+++ b/routes/pixivision.go
@@ -18,7 +18,7 @@ func PixivisionHomePage(c *fiber.Ctx) error {
data[i].Thumbnail = session.ProxyImageUrlNoEscape(c, data[i].Thumbnail)
}
- return c.Render("pixivision/index", fiber.Map{"Data": data})
+ return c.Render("pixivision", fiber.Map{"Data": data})
}
func PixivisionArticlePage(c *fiber.Ctx) error {
@@ -35,5 +35,5 @@ func PixivisionArticlePage(c *fiber.Ctx) error {
data.Items[i].Avatar = session.ProxyImageUrlNoEscape(c, data.Items[i].Avatar)
}
- return c.Render("pixivision/article", fiber.Map{"Article": data})
+ return c.Render("pixivision_article", fiber.Map{"Article": data})
}
diff --git a/routes/render_.go b/routes/render_.go
index 64c6993..3fa4a95 100644
--- a/routes/render_.go
+++ b/routes/render_.go
@@ -7,6 +7,9 @@ import (
"codeberg.org/vnpower/pixivfe/v2/core"
"codeberg.org/vnpower/pixivfe/v2/session"
+ "codeberg.org/vnpower/pixivfe/v2/utils"
+
+ "github.com/CloudyKit/jet/v6"
"github.com/gofiber/fiber/v2"
)
@@ -121,11 +124,29 @@ type Data_user struct {
// add new types above this line
// whenever you add new types, update `TestTemplates` in render_test.go to include the type in the test
-
// caution: do not use pointer in Data_* struct. faker will insert nil.
-// caution: do not name template file a.b.jet.html or it won't be able to be used here. Data_a.b is not a valid identifier.
+// caution: do not name template file a.b.jet.html or it won't be able to be used here, since Data_a.b is not a valid identifier.
-func Render[T interface{}](c *fiber.Ctx, data T) error {
+// global variable, yes.
+var views *jet.Set
+
+func InitTemplatingEngine(InDevelopment bool) {
+ if InDevelopment {
+ views = jet.NewSet(
+ jet.NewOSFileSystemLoader("assets/views"),
+ jet.InDevelopmentMode(), // disable cache
+ )
+ } else {
+ views = jet.NewSet(
+ jet.NewOSFileSystemLoader("assets/views"),
+ )
+ }
+ for fn_name, fn := range utils.GetTemplateFunctions() {
+ views.AddGlobal(fn_name, fn)
+ }
+}
+
+func Render[T any](c *fiber.Ctx, data T) error {
template_name, found := strings.CutPrefix(reflect.TypeFor[T]().Name(), "Data_")
if !found {
log.Panicf("struct name does not start with 'Data_': %s", template_name)
@@ -141,25 +162,46 @@ func Render[T interface{}](c *fiber.Ctx, data T) error {
cookies[string(name)] = value
}
- bind := StructToMap(data)
+ template, err := views.GetTemplate(template_name + ".jet.html")
+ if err != nil {
+ return err
+ }
+
+ views.Parse(template_name + ".jet.html", template.String())
+
+ variables := jet.VarMap{}
// The middleware at line 99 in `main.go` cannot bind these values below if we use this function.
- bind["BaseURL"] = c.BaseURL()
- bind["OriginalURL"] = c.OriginalURL()
- bind["PageURL"] = pageURL
- bind["LoggedIn"] = token != ""
- bind["Queries"] = c.Queries()
- bind["CookieList"] = cookies
+ variables.Set("BaseURL", c.BaseURL())
+ variables.Set("OriginalURL", c.OriginalURL())
+ variables.Set("PageURL", pageURL)
+ variables.Set("LoggedIn", token != "")
+ variables.Set("Queries", c.Queries())
+ variables.Set("CookieList", cookies)
- return c.Render(template_name, bind)
+ // Type := reflect.TypeFor[T]()
+ // for _, special_varname := range []string{"Title", "MetaAuthor", "MetaDescription", "MetaImage"} {
+ // _, has_field := Type.FieldByName(special_varname)
+ // if has_field {
+ // variables.Set(special_varname, FieldName(data, special_varname))
+ // }
+ // }
+
+ c.Context().SetContentType("text/html; charset=utf-8")
+ return template.Execute(c.Response().BodyWriter(), variables, data)
}
-func StructToMap[T interface{}](data T) map[string]interface{} {
- result := map[string]interface{}{}
+func StructToMap[T any](data T) map[string]any {
+ result := map[string]any{}
Type := reflect.TypeFor[T]()
for i := 0; i < Type.NumField(); i += 1 {
field := Type.Field(i)
- result[field.Name] = reflect.ValueOf(data).FieldByName(field.Name).Interface()
+ result[field.Name] = FieldName(data, field.Name)
}
return result
}
+
+// assumes that the field `field_name` exists, panics otherwise
+func FieldName[T any](data T, field_name string) any {
+ return reflect.ValueOf(data).FieldByName(field_name).Interface()
+}
diff --git a/routes/render_test.go b/routes/render_test.go
index 5716145..7107399 100644
--- a/routes/render_test.go
+++ b/routes/render_test.go
@@ -37,7 +37,7 @@ func TestTemplates(t *testing.T) {
var engine *jet.Engine
func TestMain(m *testing.M) {
- engine = jet.New("../assets/layout", ".jet.html")
+ engine = jet.New("../assets/views", ".jet.html")
engine.AddFuncMap(utils.GetTemplateFunctions())
// gofiber bug: no error even if the templates are invalid??? https://github.com/gofiber/template/issues/341
@@ -50,7 +50,7 @@ func TestMain(m *testing.M) {
}
// test template
-func test[T interface{}](t *testing.T) {
+func test[T any](t *testing.T) {
var data T
faker.FakeData(&data)
@@ -60,7 +60,7 @@ func test[T interface{}](t *testing.T) {
}
bindings := StructToMap(data)
- for k, v := range map[string]interface{}{
+ for k, v := range map[string]any{
"BaseURL": "",
"OriginalURL": "",
"PageURL": "",
diff --git a/utils/templateFunctions.go b/utils/templateFunctions.go
index 5b6808c..55de643 100644
--- a/utils/templateFunctions.go
+++ b/utils/templateFunctions.go
@@ -251,8 +251,8 @@ func lowercaseFirstChar(s string) string {
return strings.ToLower(s[0:1]) + s[1:]
}
-func GetTemplateFunctions() template.FuncMap {
- return template.FuncMap{
+func GetTemplateFunctions() map[string]any {
+ return map[string]any{
"parseEmojis": func(s string) template.HTML {
return ParseEmojis(s)
},
Related works
Discover artworks
An error occured
This is what you got instead ¯\_(ツ)_/¯
@@ -8,3 +10,4 @@ us by submitting an issue to the upstream repository!Newest works from people you follow
Newest works from all users
Discover novels
{{Article.Title}}
{{ range _, desc := Article.Description }} @@ -25,3 +27,4 @@Ranking calendar ({{ ThisMonth.MonthLiteral }} {{ Year }})
Settings
@@ -185,3 +187,4 @@