mirror of
https://codeberg.org/VnPower/PixivFE
synced 2024-12-06 19:16:23 +01:00
use proper jet layout feature (extends)
This commit is contained in:
@@ -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">»</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
@@ -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
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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": "",
|
||||
|
||||
@@ -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)
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user