diff --git a/assets/views/about.jet.html b/assets/views/about.jet.html index e7457b8..21bf767 100644 --- a/assets/views/about.jet.html +++ b/assets/views/about.jet.html @@ -1,4 +1,4 @@ -{{extends "layout/default.jet"}} +{{extends "layout/default.jet.html"}} {{block body()}}

About PixivFE

diff --git a/assets/views/artwork.jet.html b/assets/views/artwork.jet.html index d30d15a..ffb1e09 100644 --- a/assets/views/artwork.jet.html +++ b/assets/views/artwork.jet.html @@ -1,4 +1,4 @@ -{{extends "layout/default.jet"}} +{{extends "layout/default.jet.html"}} {{block body()}}
{{ include "components/artwork" .Illust }} diff --git a/assets/views/artworkMulti.jet.html b/assets/views/artworkMulti.jet.html index e32c0f0..02cf6f7 100644 --- a/assets/views/artworkMulti.jet.html +++ b/assets/views/artworkMulti.jet.html @@ -1,4 +1,4 @@ -{{extends "layout/default.jet"}} +{{extends "layout/default.jet.html"}} {{block body()}} {{ range Artworks }}
diff --git a/assets/views/discovery.jet.html b/assets/views/discovery.jet.html index a661b30..0f84244 100644 --- a/assets/views/discovery.jet.html +++ b/assets/views/discovery.jet.html @@ -1,4 +1,4 @@ -{{extends "layout/default.jet"}} +{{extends "layout/default.jet.html"}} {{block body()}}

Discover artworks

diff --git a/assets/views/error.jet.html b/assets/views/error.jet.html index ea8e686..bb9a28b 100644 --- a/assets/views/error.jet.html +++ b/assets/views/error.jet.html @@ -1,4 +1,4 @@ -{{extends "layout/default.jet"}} +{{extends "layout/default.jet.html"}} {{block body()}}

An error occured

diff --git a/assets/views/following.jet.html b/assets/views/following.jet.html index c740146..2a87651 100644 --- a/assets/views/following.jet.html +++ b/assets/views/following.jet.html @@ -1,4 +1,4 @@ -{{extends "layout/default.jet"}} +{{extends "layout/default.jet.html"}} {{block body()}}

Newest works from people you follow

diff --git a/assets/views/index.jet.html b/assets/views/index.jet.html index d7592d8..8c6ccd8 100644 --- a/assets/views/index.jet.html +++ b/assets/views/index.jet.html @@ -1,15 +1,15 @@ -{{extends "layout/default.jet"}} +{{extends "layout/default.jet.html"}} {{block body()}}
-{{ if isset(NoTokenData) }} +{{ if !.LoggedIn }}

Not logged in. You can login here.

Today's illustration rankings

See more
- {{ include "components/ranking-tn" NoTokenData.Contents }} + {{ include "components/ranking-tn" .NoTokenData.Contents }}
{{ else }} @@ -23,15 +23,15 @@ See more
- {{ include "components/small-tn" Data.Following }} + {{ include "components/small-tn" .Data.Following }}

Recommended works

- {{ include "components/small-tn" Data.Recommended }} + {{ include "components/small-tn" .Data.Recommended }}

- {{ range Data.RecommendByTags }} + {{ range .Data.RecommendByTags }}

Recommended illustrations tagged #{{.Name}}

{{ include "components/small-tn" .Artworks }} diff --git a/assets/views/newest.jet.html b/assets/views/newest.jet.html index 8d9ae45..67414b9 100644 --- a/assets/views/newest.jet.html +++ b/assets/views/newest.jet.html @@ -1,4 +1,4 @@ -{{extends "layout/default.jet"}} +{{extends "layout/default.jet.html"}} {{block body()}}

Newest works from all users

diff --git a/assets/views/novel.jet.html b/assets/views/novel.jet.html index 2aa6ad9..e50fc6a 100644 --- a/assets/views/novel.jet.html +++ b/assets/views/novel.jet.html @@ -1,4 +1,4 @@ -{{extends "layout/default.jet"}} +{{extends "layout/default.jet.html"}} {{block body()}}
diff --git a/assets/views/novelDiscovery.jet.html b/assets/views/novelDiscovery.jet.html index 5f438f5..d2a233b 100644 --- a/assets/views/novelDiscovery.jet.html +++ b/assets/views/novelDiscovery.jet.html @@ -1,4 +1,4 @@ -{{extends "layout/default.jet"}} +{{extends "layout/default.jet.html"}} {{block body()}}

Discover novels

diff --git a/assets/views/pixivision.jet.html b/assets/views/pixivision.jet.html index d9d21d9..dc23456 100644 --- a/assets/views/pixivision.jet.html +++ b/assets/views/pixivision.jet.html @@ -1,4 +1,4 @@ -{{extends "layout/default.jet"}} +{{extends "layout/default.jet.html"}} {{block body()}}
diff --git a/assets/views/pixivision_article.jet.html b/assets/views/pixivision_article.jet.html index 3fbb418..f7f1b1b 100644 --- a/assets/views/pixivision_article.jet.html +++ b/assets/views/pixivision_article.jet.html @@ -1,4 +1,4 @@ -{{extends "layout/default.jet"}} +{{extends "layout/default.jet.html"}} {{block body()}}

{{Article.Title}}

diff --git a/assets/views/rank.jet.html b/assets/views/rank.jet.html index 93dcd57..2deeb1d 100644 --- a/assets/views/rank.jet.html +++ b/assets/views/rank.jet.html @@ -1,4 +1,4 @@ -{{extends "layout/default.jet"}} +{{extends "layout/default.jet.html"}} {{block body()}} {{ import "components/switcher" }} diff --git a/assets/views/rankingCalendar.jet.html b/assets/views/rankingCalendar.jet.html index bc5770d..df87281 100644 --- a/assets/views/rankingCalendar.jet.html +++ b/assets/views/rankingCalendar.jet.html @@ -1,4 +1,4 @@ -{{extends "layout/default.jet"}} +{{extends "layout/default.jet.html"}} {{block body()}}

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

diff --git a/assets/views/settings.jet.html b/assets/views/settings.jet.html index 73f5a43..a9bdf92 100644 --- a/assets/views/settings.jet.html +++ b/assets/views/settings.jet.html @@ -1,4 +1,4 @@ -{{extends "layout/default.jet"}} +{{extends "layout/default.jet.html"}} {{block body()}}

Settings

diff --git a/assets/views/tag.jet.html b/assets/views/tag.jet.html index bb01fcc..baa077f 100644 --- a/assets/views/tag.jet.html +++ b/assets/views/tag.jet.html @@ -1,4 +1,4 @@ -{{extends "layout/default.jet"}} +{{extends "layout/default.jet.html"}} {{block body()}} {{ import "components/switcher" }} diff --git a/assets/views/temp.jet.html b/assets/views/temp.jet.html index a64899d..031030a 100644 --- a/assets/views/temp.jet.html +++ b/assets/views/temp.jet.html @@ -1,4 +1,4 @@ -{{extends "layout/default.jet"}} +{{extends "layout/default.jet.html"}} {{block body()}} {{ if Token }} diff --git a/assets/views/unauthorized.jet.html b/assets/views/unauthorized.jet.html index 8c539cd..041dd15 100644 --- a/assets/views/unauthorized.jet.html +++ b/assets/views/unauthorized.jet.html @@ -1,4 +1,4 @@ -{{extends "layout/default.jet"}} +{{extends "layout/default.jet.html"}} {{block body()}}

Unauthorized

diff --git a/assets/views/user.jet.html b/assets/views/user.jet.html index 4aeb98c..6246139 100644 --- a/assets/views/user.jet.html +++ b/assets/views/user.jet.html @@ -1,4 +1,4 @@ -{{extends "layout/default.jet"}} +{{extends "layout/default.jet.html"}} {{block body()}} {{ if ! User.BackgroundImage }}
diff --git a/assets/views/userAtom.jet.html b/assets/views/userAtom.jet.html index 6061299..041e030 100644 --- a/assets/views/userAtom.jet.html +++ b/assets/views/userAtom.jet.html @@ -1,4 +1,4 @@ -{{extends "layout/default.jet"}} +{{extends "layout/default.jet.html"}} {{block body()}} {* *} diff --git a/doc/dev/framework-migration.md b/doc/dev/framework-migration.md index b87a8cf..2848741 100644 --- a/doc/dev/framework-migration.md +++ b/doc/dev/framework-migration.md @@ -7,8 +7,9 @@ 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(...)`). +If you see error like ` not found in map[...]` when visiting a page, you need to add a dot before all data member access. e.g. from `Illust` to `.Illust`. -Data access must be prefixed with a dot. `.Illust` is valid. `Illust` is a variable but not data. +gofiber doesn't mention this. +In Jet, variables are accessed without a dot (`PageURL`), while data members (the `data` parameter to `Render(...)`). are accessed with a dot in front. Current templates use the variable style (`Illust`), but that is wrong. diff --git a/routes/index.go b/routes/index.go index 07dd9bd..acf28ab 100644 --- a/routes/index.go +++ b/routes/index.go @@ -18,8 +18,10 @@ func IndexPage(c *fiber.Ctx) error { return err } - return c.Render("index", fiber.Map{ - "Title": "Landing", "Data": works, + return Render(c, Data_index{ + Title: "Landing", + Data: *works, + IsLoggedIn: true, }) } @@ -28,8 +30,10 @@ func IndexPage(c *fiber.Ctx) error { if err != nil { return err } - return c.Render("index", fiber.Map{ - "Title": "Landing", "NoTokenData": works, + return Render(c, Data_index{ + Title: "Landing", + NoTokenData: works, + IsLoggedIn: false, }) } diff --git a/routes/render.go b/routes/render.go new file mode 100644 index 0000000..26aa6c7 --- /dev/null +++ b/routes/render.go @@ -0,0 +1,84 @@ +package routes + +import ( + "log" + "reflect" + "strings" + + "codeberg.org/vnpower/pixivfe/v2/session" + "codeberg.org/vnpower/pixivfe/v2/utils" + + "github.com/CloudyKit/jet/v6" + "github.com/gofiber/fiber/v2" +) + +// 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) + } + + // Pass in values that we want to be available to all pages here + token := session.GetPixivToken(c) + pageURL := c.BaseURL() + c.OriginalURL() + + cookies := map[string]string{} + for _, name := range session.AllCookieNames { + value := session.GetCookie(c, name) + cookies[string(name)] = value + } + + 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. + 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) + + c.Context().SetContentType("text/html; charset=utf-8") + return template.Execute(c.Response().BodyWriter(), variables, data) +} + +// 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] = 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_.go b/routes/render_.go deleted file mode 100644 index 3fa4a95..0000000 --- a/routes/render_.go +++ /dev/null @@ -1,207 +0,0 @@ -package routes - -import ( - "log" - "reflect" - "strings" - - "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" -) - -type Data_error struct { - Title string - Error error -} -type Data_about struct { - Time string - Version string - ImageProxy string - AcceptLanguage string -} -type Data_artwork struct { - Illust core.Illust // faker can't fill this - Title string - MetaDescription string - MetaImage string - MetaAuthor string - MetaAuthorID string -} -type Data_artworkMulti struct { - Artworks []core.Illust - Title string -} -type Data_userAtom struct { - URL string - Title string - User core.User - Category core.UserArtCategory - Updated string - PageLimit int - Page int - // MetaImage string -} - -// below are unconverted. types may be wrong. -type Data_discovery struct { - Artworks string - Title string - Queries string -} -type Data_novelDiscovery struct { - Novels string - Title string -} -type Data_index struct { - Title string - Data string -} -type Data_newest struct { - Items string - Title string -} -type Data_novel struct { - Novel string - NovelRelated string - User string - Title string - FontType string - ViewMode string - Language string -} -type Data_unauthorized struct{} -type Data_following struct { - Title string - Mode string - Artworks string - CurPage string - Page string -} - -// type Data_pixivisionindex struct { -// Data string -// } -// -// type Data_pixivisionarticle struct { -// Article string -// } -type Data_rank struct { - Title string - Page string - PageLimit int - Date string - Data string -} -type Data_rankingCalendar struct { - Title string - Render string - Mode string - Year string - MonthBefore string - MonthAfter string - ThisMonth string -} - -// type Data_settings struct { -// ProxyList string -// } -// -// type Data_tag struct { -// Title string -// } -type Data_user struct { - Title string - User string - Category string - PageLimit int - Page string - MetaImage string -} - -// 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, since Data_a.b is not a valid identifier. - -// 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) - } - - // Pass in values that we want to be available to all pages here - token := session.GetPixivToken(c) - pageURL := c.BaseURL() + c.OriginalURL() - - cookies := map[string]string{} - for _, name := range session.AllCookieNames { - value := session.GetCookie(c, name) - cookies[string(name)] = value - } - - 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. - 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) - - // 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 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] = 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 7107399..843cc29 100644 --- a/routes/render_test.go +++ b/routes/render_test.go @@ -13,25 +13,25 @@ import ( ) func TestTemplates(t *testing.T) { - test[Data_error](t) - test[Data_about](t) - test[Data_artwork](t) - test[Data_artworkMulti](t) - test[Data_discovery](t) - test[Data_novelDiscovery](t) - test[Data_index](t) - test[Data_newest](t) - test[Data_novel](t) - test[Data_unauthorized](t) - test[Data_following](t) - test[Data_pixivisionindex](t) - test[Data_pixivisionarticle](t) - test[Data_rank](t) - test[Data_rankingCalendar](t) - test[Data_settings](t) - test[Data_tag](t) - test[Data_user](t) - test[Data_userAtom](t) + autoTest[Data_error](t) + autoTest[Data_about](t) + autoTest[Data_artwork](t) + autoTest[Data_artworkMulti](t) + autoTest[Data_discovery](t) + autoTest[Data_novelDiscovery](t) + autoTest[Data_index](t) + autoTest[Data_newest](t) + autoTest[Data_novel](t) + autoTest[Data_unauthorized](t) + autoTest[Data_following](t) + autoTest[Data_pixivision_index](t) + autoTest[Data_pixivision_article](t) + autoTest[Data_rank](t) + autoTest[Data_rankingCalendar](t) + autoTest[Data_settings](t) + autoTest[Data_tag](t) + autoTest[Data_user](t) + autoTest[Data_userAtom](t) } var engine *jet.Engine @@ -49,16 +49,19 @@ func TestMain(m *testing.M) { m.Run() } -// test template -func test[T any](t *testing.T) { +// autoTest template with fake data +func autoTest[T any](t *testing.T) { var data T faker.FakeData(&data) + manualTest(t, data) +} +func manualTest[T any](t *testing.T, data T) { route_name, found := strings.CutPrefix(reflect.TypeFor[T]().Name(), "Data_") if !found { log.Panicf("struct name does not start with 'Data_': %s", route_name) } - bindings := StructToMap(data) + bindings := structToMap(data) for k, v := range map[string]any{ "BaseURL": "", @@ -78,4 +81,3 @@ func test[T any](t *testing.T) { t.Errorf("while rendering template %s: %v", template_name, err) } } - diff --git a/routes/render_types.go b/routes/render_types.go new file mode 100644 index 0000000..e6ef3a9 --- /dev/null +++ b/routes/render_types.go @@ -0,0 +1,120 @@ +package routes + +import "codeberg.org/vnpower/pixivfe/v2/core" + +type Data_error struct { + Title string + Error error +} +type Data_about struct { + Time string + Version string + ImageProxy string + AcceptLanguage string +} +type Data_artwork struct { + Illust core.Illust // faker can't fill this + Title string + MetaDescription string + MetaImage string + MetaAuthor string + MetaAuthorID string +} +type Data_artworkMulti struct { + Artworks []core.Illust + Title string +} +type Data_userAtom struct { + URL string + Title string + User core.User + Category core.UserArtCategory + Updated string + PageLimit int + Page int + // MetaImage string +} + +type Data_index struct { + Title string + IsLoggedIn bool + Data core.LandingArtworks + NoTokenData core.Ranking +} + +// below are unconverted. types may be wrong. +type Data_discovery struct { + Artworks string + Title string + Queries string +} +type Data_novelDiscovery struct { + Novels string + Title string +} +type Data_newest struct { + Items string + Title string +} +type Data_novel struct { + Novel string + NovelRelated string + User string + Title string + FontType string + ViewMode string + Language string +} +type Data_unauthorized struct{} +type Data_following struct { + Title string + Mode string + Artworks string + CurPage string + Page string +} + +// type Data_pixivisionindex struct { +// Data string +// } +// +// type Data_pixivisionarticle struct { +// Article string +// } +type Data_rank struct { + Title string + Page string + PageLimit int + Date string + Data string +} +type Data_rankingCalendar struct { + Title string + Render string + Mode string + Year string + MonthBefore string + MonthAfter string + ThisMonth string +} + +// type Data_settings struct { +// ProxyList string +// } +// +// type Data_tag struct { +// Title string +// } +type Data_user struct { + Title string + User string + Category string + PageLimit int + Page string + MetaImage string +} + +// 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, since Data_a.b is not a valid identifier.