diff --git a/assets/layout/about.jet.html b/assets/views/about.jet.html similarity index 94% rename from assets/layout/about.jet.html rename to assets/views/about.jet.html index b531a68..e7457b8 100644 --- a/assets/layout/about.jet.html +++ b/assets/views/about.jet.html @@ -1,3 +1,5 @@ +{{extends "layout/default.jet"}} +{{block body()}}

About PixivFE

PixivFE is a Pixiv frontend. It lets you enjoy art on Pixiv without needless interruption.

@@ -23,3 +25,4 @@
+{{end}} diff --git a/assets/layout/artwork.jet.html b/assets/views/artwork.jet.html similarity index 70% rename from assets/layout/artwork.jet.html rename to assets/views/artwork.jet.html index e23d586..d30d15a 100644 --- a/assets/layout/artwork.jet.html +++ b/assets/views/artwork.jet.html @@ -1,16 +1,18 @@ +{{extends "layout/default.jet"}} +{{block body()}}
- {{ include "components/artwork" Illust }} + {{ include "components/artwork" .Illust }}
- {{ Illust.User.Name }} Other works by {{ Illust.User.Name }} + {{ .Illust.User.Name }} Other works by {{ .Illust.User.Name }} » - {{ combinedUrl := "/artworks-multi/" + joinArtworkIds(Illust.RecentWorks) }} + {{ combinedUrl := "/artworks-multi/" + joinArtworkIds(.Illust.RecentWorks) }}
- {{ range Illust.RecentWorks }} + {{ range .Illust.RecentWorks }}
{{ include "components/thumbnail-dt" . }}
@@ -20,11 +22,11 @@
Comments - {{ if Illust.CommentDisabled == 1 }} + {{ if .Illust.CommentDisabled == 1 }}

The creator turned comments off

- {{ else if Illust.Comments == 0 }} + {{ else if .Illust.Comments == 0 }}

There are no comments yet

- {{ else }} {{ range Illust.CommentsList }} + {{ else }} {{ range .Illust.CommentsList }}
{{ .AuthorName }} @@ -49,6 +51,7 @@

Related works

- {{ include "components/small-tn" Illust.RelatedWorks }} + {{ include "components/small-tn" .Illust.RelatedWorks }}
+{{end}} diff --git a/assets/layout/artworkMulti.jet.html b/assets/views/artworkMulti.jet.html similarity index 58% rename from assets/layout/artworkMulti.jet.html rename to assets/views/artworkMulti.jet.html index 1200c1d..e32c0f0 100644 --- a/assets/layout/artworkMulti.jet.html +++ b/assets/views/artworkMulti.jet.html @@ -1,5 +1,7 @@ +{{extends "layout/default.jet"}} +{{block body()}} {{ range Artworks }}
{{ include "components/artwork" . }}
-{{ end }} \ No newline at end of file +{{ end }}{{end}} diff --git a/assets/layout/components/artwork.jet.html b/assets/views/components/artwork.jet.html similarity index 100% rename from assets/layout/components/artwork.jet.html rename to assets/views/components/artwork.jet.html diff --git a/assets/layout/components/novel-tn.jet.html b/assets/views/components/novel-tn.jet.html similarity index 100% rename from assets/layout/components/novel-tn.jet.html rename to assets/views/components/novel-tn.jet.html diff --git a/assets/layout/components/ranking-tn.jet.html b/assets/views/components/ranking-tn.jet.html similarity index 100% rename from assets/layout/components/ranking-tn.jet.html rename to assets/views/components/ranking-tn.jet.html diff --git a/assets/layout/components/small-tn.jet.html b/assets/views/components/small-tn.jet.html similarity index 100% rename from assets/layout/components/small-tn.jet.html rename to assets/views/components/small-tn.jet.html diff --git a/assets/layout/components/switcher.jet.html b/assets/views/components/switcher.jet.html similarity index 100% rename from assets/layout/components/switcher.jet.html rename to assets/views/components/switcher.jet.html diff --git a/assets/layout/components/thumbnail-at.jet.html b/assets/views/components/thumbnail-at.jet.html similarity index 100% rename from assets/layout/components/thumbnail-at.jet.html rename to assets/views/components/thumbnail-at.jet.html diff --git a/assets/layout/components/thumbnail-dt.jet.html b/assets/views/components/thumbnail-dt.jet.html similarity index 100% rename from assets/layout/components/thumbnail-dt.jet.html rename to assets/views/components/thumbnail-dt.jet.html diff --git a/assets/layout/components/thumbnail-tt.jet.html b/assets/views/components/thumbnail-tt.jet.html similarity index 100% rename from assets/layout/components/thumbnail-tt.jet.html rename to assets/views/components/thumbnail-tt.jet.html diff --git a/assets/layout/discovery.jet.html b/assets/views/discovery.jet.html similarity index 93% rename from assets/layout/discovery.jet.html rename to assets/views/discovery.jet.html index 8e56756..a661b30 100644 --- a/assets/layout/discovery.jet.html +++ b/assets/views/discovery.jet.html @@ -1,3 +1,5 @@ +{{extends "layout/default.jet"}} +{{block body()}}
+{{end}} diff --git a/assets/layout/error.jet.html b/assets/views/error.jet.html similarity index 85% rename from assets/layout/error.jet.html rename to assets/views/error.jet.html index fad2587..ea8e686 100644 --- a/assets/layout/error.jet.html +++ b/assets/views/error.jet.html @@ -1,3 +1,5 @@ +{{extends "layout/default.jet"}} +{{block body()}}

An error occured

This is what you got instead ¯\_(ツ)_/¯

@@ -8,3 +10,4 @@ us by submitting an issue to the upstream repository!

+{{end}} diff --git a/assets/layout/following.jet.html b/assets/views/following.jet.html similarity index 92% rename from assets/layout/following.jet.html rename to assets/views/following.jet.html index f2352cd..c740146 100644 --- a/assets/layout/following.jet.html +++ b/assets/views/following.jet.html @@ -1,3 +1,5 @@ +{{extends "layout/default.jet"}} +{{block body()}}

Newest works from people you follow

@@ -14,3 +16,4 @@ {{ raw: createPaginator(url, "#checkpoint", Page, -1) }}
+{{end}} diff --git a/assets/layout/index.jet.html b/assets/views/index.jet.html similarity index 95% rename from assets/layout/index.jet.html rename to assets/views/index.jet.html index 8deeb68..d7592d8 100644 --- a/assets/layout/index.jet.html +++ b/assets/views/index.jet.html @@ -1,3 +1,5 @@ +{{extends "layout/default.jet"}} +{{block body()}}
{{ if isset(NoTokenData) }} @@ -39,3 +41,4 @@ {{ end }}
+{{end}} diff --git a/assets/layout/_layout.jet.html b/assets/views/layout/default.jet.html similarity index 90% rename from assets/layout/_layout.jet.html rename to assets/views/layout/default.jet.html index bf18218..99b8f7a 100644 --- a/assets/layout/_layout.jet.html +++ b/assets/views/layout/default.jet.html @@ -6,7 +6,7 @@ - {{ title := "" }} {{ if isset(Title) }} {{ title = Title }} {{ else }} {{ + {{ title := "" }} {{ if isset(.Title) }} {{ title = .Title }} {{ else }} {{ title = "PixivFE" }} {{ end }} {{ title }} - PixivFE @@ -23,19 +23,19 @@ - {{ if isset(MetaAuthor) }} + {{ if isset(.MetaAuthor) }} + href="{{ BaseURL }}/oembed?a={{ .MetaAuthor }}&u={{ BaseURL }}/users/{{ .MetaAuthorID }}"> {{else}} {{end}} - {{ if isset(MetaDescription) }} - + {{ if isset(.MetaDescription) }} + {{end}} - {{ if isset(MetaDescription) }} - + {{ if isset(.MetaImage) }} + {{ end }} @@ -99,7 +99,7 @@
- {{ embed() }} + {{yield body()}}
{{CookieList["pixivfe-ArtworkPreview"]}} diff --git a/assets/layout/newest.jet.html b/assets/views/newest.jet.html similarity index 85% rename from assets/layout/newest.jet.html rename to assets/views/newest.jet.html index 39bd621..8d9ae45 100644 --- a/assets/layout/newest.jet.html +++ b/assets/views/newest.jet.html @@ -1,3 +1,5 @@ +{{extends "layout/default.jet"}} +{{block body()}}

Newest works from all users

@@ -7,3 +9,4 @@
{{ include "components/small-tn" Items }}
+{{end}} diff --git a/assets/layout/novel.jet.html b/assets/views/novel.jet.html similarity index 98% rename from assets/layout/novel.jet.html rename to assets/views/novel.jet.html index bdee99c..2aa6ad9 100644 --- a/assets/layout/novel.jet.html +++ b/assets/views/novel.jet.html @@ -1,3 +1,5 @@ +{{extends "layout/default.jet"}} +{{block body()}}
@@ -127,3 +129,4 @@ {{ end }}
+{{end}} diff --git a/assets/layout/novelDiscovery.jet.html b/assets/views/novelDiscovery.jet.html similarity index 92% rename from assets/layout/novelDiscovery.jet.html rename to assets/views/novelDiscovery.jet.html index ab64978..5f438f5 100644 --- a/assets/layout/novelDiscovery.jet.html +++ b/assets/views/novelDiscovery.jet.html @@ -1,3 +1,5 @@ +{{extends "layout/default.jet"}} +{{block body()}}

Discover novels

@@ -21,3 +23,4 @@ Reload
+{{end}} diff --git a/assets/layout/pixivision/index.jet.html b/assets/views/pixivision.jet.html similarity index 92% rename from assets/layout/pixivision/index.jet.html rename to assets/views/pixivision.jet.html index fcb118b..d9d21d9 100644 --- a/assets/layout/pixivision/index.jet.html +++ b/assets/views/pixivision.jet.html @@ -1,3 +1,5 @@ +{{extends "layout/default.jet"}} +{{block body()}}
{{ range _, article := Data }} @@ -24,3 +26,4 @@ {{ end }}
+{{end}} diff --git a/assets/layout/pixivision/article.jet.html b/assets/views/pixivision_article.jet.html similarity index 92% rename from assets/layout/pixivision/article.jet.html rename to assets/views/pixivision_article.jet.html index c19a6f6..3fbb418 100644 --- a/assets/layout/pixivision/article.jet.html +++ b/assets/views/pixivision_article.jet.html @@ -1,3 +1,5 @@ +{{extends "layout/default.jet"}} +{{block body()}}

{{Article.Title}}

{{ range _, desc := Article.Description }} @@ -25,3 +27,4 @@
{{ end }}
+{{end}} diff --git a/assets/layout/rank.jet.html b/assets/views/rank.jet.html similarity index 98% rename from assets/layout/rank.jet.html rename to assets/views/rank.jet.html index 5c86bf6..93dcd57 100644 --- a/assets/layout/rank.jet.html +++ b/assets/views/rank.jet.html @@ -1,3 +1,5 @@ +{{extends "layout/default.jet"}} +{{block body()}} {{ import "components/switcher" }}
@@ -82,3 +84,4 @@ {{ raw: createPaginator(url, "#checkpoint", Page, PageLimit) }}
+{{end}} diff --git a/assets/layout/rankingCalendar.jet.html b/assets/views/rankingCalendar.jet.html similarity index 97% rename from assets/layout/rankingCalendar.jet.html rename to assets/views/rankingCalendar.jet.html index a78c9df..bc5770d 100644 --- a/assets/layout/rankingCalendar.jet.html +++ b/assets/views/rankingCalendar.jet.html @@ -1,3 +1,5 @@ +{{extends "layout/default.jet"}} +{{block body()}}

Ranking calendar ({{ ThisMonth.MonthLiteral }} {{ Year }})

@@ -56,3 +58,4 @@ Next month
+{{end}} diff --git a/assets/layout/settings.jet.html b/assets/views/settings.jet.html similarity index 99% rename from assets/layout/settings.jet.html rename to assets/views/settings.jet.html index 18da5ef..73f5a43 100644 --- a/assets/layout/settings.jet.html +++ b/assets/views/settings.jet.html @@ -1,3 +1,5 @@ +{{extends "layout/default.jet"}} +{{block body()}}

Settings

@@ -185,3 +187,4 @@

+{{end}} diff --git a/assets/layout/tag.jet.html b/assets/views/tag.jet.html similarity index 98% rename from assets/layout/tag.jet.html rename to assets/views/tag.jet.html index 6b7a36a..bb01fcc 100644 --- a/assets/layout/tag.jet.html +++ b/assets/views/tag.jet.html @@ -1,3 +1,5 @@ +{{extends "layout/default.jet"}} +{{block body()}} {{ import "components/switcher" }}
@@ -116,3 +118,4 @@ {{ raw: createPaginator(url, "#checkpoint", Page, -1) }}
+{{end}} diff --git a/assets/layout/temp.jet.html b/assets/views/temp.jet.html similarity index 98% rename from assets/layout/temp.jet.html rename to assets/views/temp.jet.html index 7eea608..a64899d 100644 --- a/assets/layout/temp.jet.html +++ b/assets/views/temp.jet.html @@ -1,3 +1,5 @@ +{{extends "layout/default.jet"}} +{{block body()}} {{ if Token }}
@@ -162,3 +164,4 @@
*} {{ end }} +{{end}} diff --git a/assets/layout/unauthorized.jet.html b/assets/views/unauthorized.jet.html similarity index 71% rename from assets/layout/unauthorized.jet.html rename to assets/views/unauthorized.jet.html index ba1c19f..8c539cd 100644 --- a/assets/layout/unauthorized.jet.html +++ b/assets/views/unauthorized.jet.html @@ -1,5 +1,8 @@ +{{extends "layout/default.jet"}} +{{block body()}}

Unauthorized

You need to log in to use this feature.

+{{end}} diff --git a/assets/layout/user.jet.html b/assets/views/user.jet.html similarity index 98% rename from assets/layout/user.jet.html rename to assets/views/user.jet.html index 8a4e357..4aeb98c 100644 --- a/assets/layout/user.jet.html +++ b/assets/views/user.jet.html @@ -1,3 +1,5 @@ +{{extends "layout/default.jet"}} +{{block body()}} {{ if ! User.BackgroundImage }}
{{ else }} @@ -82,3 +84,4 @@ {{ raw: createPaginator(url, "#checkpoint", Page, PageLimit) }}
+{{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) },