use proper jet layout feature (extends)

This commit is contained in:
iacore
2024-08-24 08:56:42 +00:00
parent 10134b989f
commit 822a157a6e
37 changed files with 161 additions and 50 deletions
@@ -1,3 +1,5 @@
{{extends "layout/default.jet"}}
{{block body()}}
<div class="container">
<h1>About PixivFE</h1>
<p>PixivFE is a Pixiv frontend. It lets you enjoy art on Pixiv without needless interruption.</p>
@@ -23,3 +25,4 @@
</li>
</ul>
</div>
{{end}}
@@ -1,16 +1,18 @@
{{extends "layout/default.jet"}}
{{block body()}}
<div class="container illust" id="checkpoint">
{{ include "components/artwork" Illust }}
{{ include "components/artwork" .Illust }}
<div class="illust-other-works">
<div class="artwork-container-header">
<a class="illust-other-works-author" href="/users/{{ Illust.User.ID }}"><img src="{{ Illust.User.Avatar }}"
alt="{{ Illust.User.Name }}" /> Other works by {{ Illust.User.Name }}
<a class="illust-other-works-author" href="/users/{{ .Illust.User.ID }}"><img src="{{ .Illust.User.Avatar }}"
alt="{{ .Illust.User.Name }}" /> Other works by {{ .Illust.User.Name }}
<span class="special-symbol">&raquo;</span>
</a>
{{ combinedUrl := "/artworks-multi/" + joinArtworkIds(Illust.RecentWorks) }}
{{ combinedUrl := "/artworks-multi/" + joinArtworkIds(.Illust.RecentWorks) }}
<div class="artwork-actions"><a href="{{combinedUrl}}">View all</a></div>
</div>
<div class="artwork-container-scroll">
{{ range Illust.RecentWorks }}
{{ range .Illust.RecentWorks }}
<div class="artwork-small artwork">
{{ include "components/thumbnail-dt" . }}
</div>
@@ -20,11 +22,11 @@
<hr />
<details>
<summary>Comments</summary>
{{ if Illust.CommentDisabled == 1 }}
{{ if .Illust.CommentDisabled == 1 }}
<p>The creator turned comments off</p>
{{ else if Illust.Comments == 0 }}
{{ else if .Illust.Comments == 0 }}
<p>There are no comments yet</p>
{{ else }} {{ range Illust.CommentsList }}
{{ else }} {{ range .Illust.CommentsList }}
<div class="comment">
<a href="/users/{{ .AuthorID }}" class="comment-author">
<img class="comment-avatar" src="{{ .Avatar }}" alt="{{ .AuthorName }}" />
@@ -49,6 +51,7 @@
<div class="container">
<h2>Related works</h2>
<div class="artwork-container">
{{ include "components/small-tn" Illust.RelatedWorks }}
{{ include "components/small-tn" .Illust.RelatedWorks }}
</div>
</div>
{{end}}
@@ -1,5 +1,7 @@
{{extends "layout/default.jet"}}
{{block body()}}
{{ range Artworks }}
<div class="container illust">
{{ include "components/artwork" . }}
</div>
{{ end }}
{{ end }}{{end}}
@@ -1,3 +1,5 @@
{{extends "layout/default.jet"}}
{{block body()}}
<div class="container">
<h1>Discover artworks</h1>
<div class="switcher">
@@ -18,3 +20,4 @@
<a href="" class="pagination-button">Reload</a>
</div>
</div>
{{end}}
@@ -1,3 +1,5 @@
{{extends "layout/default.jet"}}
{{block body()}}
<div class="container error">
<h1>An error occured</h1>
<p>This is what you got instead ¯\_(ツ)_/¯</p>
@@ -8,3 +10,4 @@
us by submitting an issue to the <a href="https://codeberg.org/VnPower/PixivFE/issues/new">upstream repository</a>!
</p>
</div>
{{end}}
@@ -1,3 +1,5 @@
{{extends "layout/default.jet"}}
{{block body()}}
<div class="container">
<h1>Newest works from people you follow</h1>
<div class="switcher">
@@ -14,3 +16,4 @@
{{ raw: createPaginator(url, "#checkpoint", Page, -1) }}
</div>
</div>
{{end}}
@@ -1,3 +1,5 @@
{{extends "layout/default.jet"}}
{{block body()}}
<div class="container">
{{ if isset(NoTokenData) }}
@@ -39,3 +41,4 @@
{{ end }}
</div>
{{end}}
@@ -6,7 +6,7 @@
<meta name="description" content="View this page on PixivFE." />
<meta name="viewport" content="width=device-width, initial-scale=1" />
{{ title := "" }} {{ if isset(Title) }} {{ title = Title }} {{ else }} {{
{{ title := "" }} {{ if isset(.Title) }} {{ title = .Title }} {{ else }} {{
title = "PixivFE" }} {{ end }}
<title>{{ title }} - PixivFE</title>
@@ -23,19 +23,19 @@
<meta content="summary_large_image" name="twitter:card" />
<meta property="og:url" content="{{ PageURL }}" />
{{ if isset(MetaAuthor) }}
{{ if isset(.MetaAuthor) }}
<link rel="alternate" type="application/json+oembed"
href="{{ BaseURL }}/oembed?a={{ MetaAuthor }}&u={{ BaseURL }}/users/{{ MetaAuthorID }}">
href="{{ BaseURL }}/oembed?a={{ .MetaAuthor }}&u={{ BaseURL }}/users/{{ .MetaAuthorID }}">
{{else}}
<link rel="alternate" type="application/json+oembed" href="{{ BaseURL }}/oembed">
{{end}}
{{ if isset(MetaDescription) }}
<meta property="og:description" content="{{MetaDescription}}" />
{{ if isset(.MetaDescription) }}
<meta property="og:description" content="{{.MetaDescription}}" />
{{end}}
{{ if isset(MetaDescription) }}
<meta property="og:image" content="{{ MetaImage }}" />
{{ if isset(.MetaImage) }}
<meta property="og:image" content="{{ .MetaImage }}" />
{{ end }}
</head>
@@ -99,7 +99,7 @@
</nav>
<script src="/js/on-page-load.js"></script>
<main>
{{ embed() }}
{{yield body()}}
</main>
<div id="site-config">
<span id="artworkPreview">{{CookieList["pixivfe-ArtworkPreview"]}}</span>
@@ -1,3 +1,5 @@
{{extends "layout/default.jet"}}
{{block body()}}
<div class="container">
<h1>Newest works from all users</h1>
<div class="switcher">
@@ -7,3 +9,4 @@
</div>
<div class="artwork-container">{{ include "components/small-tn" Items }}</div>
</div>
{{end}}
@@ -1,3 +1,5 @@
{{extends "layout/default.jet"}}
{{block body()}}
<div class="container novel" id="checkpoint">
<div class="novel-detail">
<div class="novel-cover">
@@ -127,3 +129,4 @@
{{ end }}
</div>
</div>
{{end}}
@@ -1,3 +1,5 @@
{{extends "layout/default.jet"}}
{{block body()}}
<div class="container">
<h1>Discover novels</h1>
<div class="switcher">
@@ -21,3 +23,4 @@
<a href="" class="pagination-button">Reload</a>
</div>
</div>
{{end}}
@@ -1,3 +1,5 @@
{{extends "layout/default.jet"}}
{{block body()}}
<div class="container">
<div class="vision-container">
{{ range _, article := Data }}
@@ -24,3 +26,4 @@
{{ end }}
</div>
</div>
{{end}}
@@ -1,3 +1,5 @@
{{extends "layout/default.jet"}}
{{block body()}}
<div class="container illust">
<h2>{{Article.Title}}</h2>
{{ range _, desc := Article.Description }}
@@ -25,3 +27,4 @@
</div>
{{ end }}
</div>
{{end}}
@@ -1,3 +1,5 @@
{{extends "layout/default.jet"}}
{{block body()}}
{{ import "components/switcher" }}
<div class="container">
@@ -82,3 +84,4 @@
{{ raw: createPaginator(url, "#checkpoint", Page, PageLimit) }}
</div>
</div>
{{end}}
@@ -1,3 +1,5 @@
{{extends "layout/default.jet"}}
{{block body()}}
<div class="container">
<h1>Ranking calendar ({{ ThisMonth.MonthLiteral }} {{ Year }})</h1>
<div class="switcher">
@@ -56,3 +58,4 @@
<a href="{{ url }}{{ MonthAfter.Link }}" class="pagination-button ">Next month</a>
</div>
</div>
{{end}}
@@ -1,3 +1,5 @@
{{extends "layout/default.jet"}}
{{block body()}}
<div class="container" data-hx-boost="false">
<h1>Settings</h1>
<p>
@@ -185,3 +187,4 @@
<input class="critical" type="submit" value="Reset all cookies/preferences" />
</form>
</div>
{{end}}
@@ -1,3 +1,5 @@
{{extends "layout/default.jet"}}
{{block body()}}
{{ import "components/switcher" }}
<div class="container">
@@ -116,3 +118,4 @@
{{ raw: createPaginator(url, "#checkpoint", Page, -1) }}
</div>
</div>
{{end}}
@@ -1,3 +1,5 @@
{{extends "layout/default.jet"}}
{{block body()}}
{{ if Token }}
<div class="switcher">
@@ -162,3 +164,4 @@
<br /> *}
{{ end }}
{{end}}
@@ -1,5 +1,8 @@
{{extends "layout/default.jet"}}
{{block body()}}
<div class="container error">
<h1>Unauthorized</h1>
<p>You need to <a href="/settings#login">log in</a> to use this feature.</p>
</div>
{{end}}
@@ -1,3 +1,5 @@
{{extends "layout/default.jet"}}
{{block body()}}
{{ if ! User.BackgroundImage }}
<div class="background-cover"></div>
{{ else }}
@@ -82,3 +84,4 @@
{{ raw: createPaginator(url, "#checkpoint", Page, PageLimit) }}
</div>
</div>
{{end}}
@@ -1,3 +1,5 @@
{{extends "layout/default.jet"}}
{{block body()}}
<?xml version="1.0" encoding="utf-8"?>{* <?xml-stylesheet href="/feed.xsl" type="text/xsl"?> *}
<feed xmlns="http://www.w3.org/2005/Atom">
<id>{{ BaseURL }}/users/{{ User.ID }}/{{ Category }}</id>
@@ -40,4 +42,4 @@
</content>
</entry>
{{ end }}
</feed>
</feed>{{end}}
+5 -5
View File
@@ -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"`
+1 -1
View File
@@ -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
+11 -3
View File
@@ -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.
+4 -1
View File
@@ -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",
+2 -2
View File
@@ -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})
}
+56 -14
View File
@@ -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()
}
+3 -3
View File
@@ -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": "",
+2 -2
View File
@@ -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)
},