finish fixing template method calls/chaining, simplify templates
This commit is contained in:
parent
b5e3413c63
commit
ddc5bd5f6e
BIN
data/dk.db
BIN
data/dk.db
Binary file not shown.
@ -1,6 +1,7 @@
|
||||
package markdown
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"html"
|
||||
"strings"
|
||||
)
|
||||
@ -40,6 +41,17 @@ func MarkdownToHTML(s string) string {
|
||||
return `<div class="md-lib">` + s + "</div>"
|
||||
}
|
||||
|
||||
// MarkdownTemplateFunc is a TemplateFunc-compatible wrapper for MarkdownToHTML
|
||||
func MarkdownTemplateFunc(args ...any) any {
|
||||
if len(args) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
// Convert first argument to string
|
||||
input := fmt.Sprintf("%v", args[0])
|
||||
return MarkdownToHTML(input)
|
||||
}
|
||||
|
||||
func replaceCodeSpans(s string) string {
|
||||
for {
|
||||
start := strings.Index(s, "`")
|
||||
|
@ -17,7 +17,6 @@ import (
|
||||
type ThreadInfo struct {
|
||||
Thread *forum.Forum
|
||||
Author *users.User
|
||||
AuthorClass string
|
||||
LastReplyBy *users.User
|
||||
}
|
||||
|
||||
@ -25,8 +24,6 @@ type ThreadInfo struct {
|
||||
type PostInfo struct {
|
||||
Post *forum.Forum
|
||||
Author *users.User
|
||||
AuthorClass string
|
||||
Content string // Pre-processed markdown content
|
||||
}
|
||||
|
||||
func RegisterForumRoutes(app *sushi.App) {
|
||||
@ -68,8 +65,6 @@ func index(ctx sushi.Ctx) {
|
||||
author = &users.User{Username: "[Deleted]", ClassID: 1}
|
||||
}
|
||||
|
||||
authorClass := author.Class().Name
|
||||
|
||||
// Get last reply author if there are replies
|
||||
var lastReplyBy *users.User
|
||||
if thread.Replies > 0 {
|
||||
@ -83,7 +78,6 @@ func index(ctx sushi.Ctx) {
|
||||
threadInfos = append(threadInfos, ThreadInfo{
|
||||
Thread: thread,
|
||||
Author: author,
|
||||
AuthorClass: authorClass,
|
||||
LastReplyBy: lastReplyBy,
|
||||
})
|
||||
}
|
||||
@ -161,8 +155,6 @@ func showThread(ctx sushi.Ctx) {
|
||||
if threadAuthor == nil {
|
||||
threadAuthor = &users.User{Username: "[Deleted]", Level: 1, ClassID: 1}
|
||||
}
|
||||
threadAuthorClass := threadAuthor.Class().Name
|
||||
threadContent := markdown.MarkdownToHTML(thread.Content)
|
||||
|
||||
// Get replies with pagination
|
||||
perPage := 30
|
||||
@ -174,21 +166,17 @@ func showThread(ctx sushi.Ctx) {
|
||||
replies = make([]*forum.Forum, 0)
|
||||
}
|
||||
|
||||
// Build reply info with authors and processed content
|
||||
// Build reply info with authors
|
||||
var replyInfos []PostInfo
|
||||
for _, reply := range replies {
|
||||
author, _ := users.Find(reply.Author)
|
||||
if author == nil {
|
||||
author = &users.User{Username: "[Deleted]", Level: 1, ClassID: 1}
|
||||
}
|
||||
authorClass := author.Class().Name
|
||||
content := markdown.MarkdownToHTML(reply.Content)
|
||||
|
||||
replyInfos = append(replyInfos, PostInfo{
|
||||
Post: reply,
|
||||
Author: author,
|
||||
AuthorClass: authorClass,
|
||||
Content: content,
|
||||
})
|
||||
}
|
||||
|
||||
@ -199,14 +187,13 @@ func showThread(ctx sushi.Ctx) {
|
||||
|
||||
components.RenderPage(ctx, thread.Title, "forum/thread.html", map[string]any{
|
||||
"thread": thread,
|
||||
"threadContent": threadContent,
|
||||
"threadAuthor": threadAuthor,
|
||||
"threadAuthorClass": threadAuthorClass,
|
||||
"replyInfos": replyInfos,
|
||||
"currentPage": page,
|
||||
"totalPages": totalPages,
|
||||
"hasNext": page < totalPages,
|
||||
"hasPrev": page > 1,
|
||||
"markdown": markdown.MarkdownToHTML,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -25,8 +25,6 @@ type TemplateFunc func(args ...any) any
|
||||
var (
|
||||
funcRegistry = make(map[string]TemplateFunc)
|
||||
funcMutex sync.RWMutex
|
||||
methodCache = make(map[string]reflect.Method)
|
||||
cacheMutex sync.RWMutex
|
||||
)
|
||||
|
||||
func init() {
|
||||
@ -465,25 +463,11 @@ func (t *Template) callMethod(obj any, methodCall string, data map[string]any) a
|
||||
argsStr := methodCall[parenIdx+1 : len(methodCall)-1]
|
||||
|
||||
rv := reflect.ValueOf(obj)
|
||||
objType := rv.Type()
|
||||
|
||||
// Check method cache
|
||||
cacheKey := fmt.Sprintf("%s.%s", objType.String(), methodName)
|
||||
cacheMutex.RLock()
|
||||
method, cached := methodCache[cacheKey]
|
||||
cacheMutex.RUnlock()
|
||||
|
||||
if !cached {
|
||||
method = rv.MethodByName(methodName)
|
||||
method := rv.MethodByName(methodName)
|
||||
if !method.IsValid() {
|
||||
return nil
|
||||
}
|
||||
|
||||
cacheMutex.Lock()
|
||||
methodCache[cacheKey] = method
|
||||
cacheMutex.Unlock()
|
||||
}
|
||||
|
||||
args := t.parseArgs(argsStr, data)
|
||||
methodType := method.Type()
|
||||
|
||||
@ -523,7 +507,7 @@ func (t *Template) parseArgs(argsStr string, data map[string]any) []any {
|
||||
inQuotes := false
|
||||
parenLevel := 0
|
||||
|
||||
for i, r := range argsStr {
|
||||
for _, r := range argsStr {
|
||||
switch r {
|
||||
case '"':
|
||||
inQuotes = !inQuotes
|
||||
@ -682,12 +666,20 @@ func (t *Template) getNestedValue(data map[string]any, path string) any {
|
||||
var current any = data
|
||||
|
||||
for i, key := range keys {
|
||||
// Check for method call
|
||||
// Check for method call at any point in the chain
|
||||
if strings.Contains(key, "(") && strings.HasSuffix(key, ")") {
|
||||
return t.callMethod(current, key, data)
|
||||
result := t.callMethod(current, key, data)
|
||||
if i == len(keys)-1 {
|
||||
// This was the final key, return the method result
|
||||
return result
|
||||
}
|
||||
// Continue with the method result for further chaining
|
||||
current = t.convertToStringMap(result)
|
||||
continue
|
||||
}
|
||||
|
||||
if i == len(keys)-1 {
|
||||
// Final key - get field/value
|
||||
switch v := current.(type) {
|
||||
case map[string]any:
|
||||
return v[key]
|
||||
@ -696,6 +688,7 @@ func (t *Template) getNestedValue(data map[string]any, path string) any {
|
||||
}
|
||||
}
|
||||
|
||||
// Intermediate key - get next object in chain
|
||||
var next any
|
||||
switch v := current.(type) {
|
||||
case map[string]any:
|
||||
|
2
main.go
2
main.go
@ -13,6 +13,7 @@ import (
|
||||
|
||||
"dk/internal/control"
|
||||
"dk/internal/database"
|
||||
"dk/internal/helpers/markdown"
|
||||
"dk/internal/models/users"
|
||||
"dk/internal/routes"
|
||||
"dk/internal/template"
|
||||
@ -143,6 +144,7 @@ func start(port string) error {
|
||||
defer control.Save()
|
||||
|
||||
template.InitializeCache(cwd)
|
||||
template.RegisterFunc("md", markdown.MarkdownTemplateFunc)
|
||||
|
||||
authMW := auth.New(getUserByID)
|
||||
|
||||
|
@ -9,32 +9,32 @@
|
||||
</div>
|
||||
|
||||
{if #threadInfos > 0}
|
||||
<table style="width: 100%; border-collapse: collapse;">
|
||||
<table>
|
||||
<thead>
|
||||
<tr style="background-color: #f5f5f5;">
|
||||
<th style="padding: 10px; text-align: left; border-bottom: 1px solid #ddd;">Thread</th>
|
||||
<th style="padding: 10px; text-align: center; border-bottom: 1px solid #ddd; width: 100px;">Replies</th>
|
||||
<th style="padding: 10px; text-align: left; border-bottom: 1px solid #ddd; width: 200px;">Last Reply</th>
|
||||
<tr>
|
||||
<th>Thread</th>
|
||||
<th>Replies</th>
|
||||
<th>Last Reply</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{for threadInfo in threadInfos}
|
||||
<tr style="border-bottom: 1px solid #eee;">
|
||||
<td style="padding: 10px;">
|
||||
<div style="font-weight: bold; margin-bottom: 5px;">
|
||||
<a href="/forum/{threadInfo.Thread.ID}" style="text-decoration: none; color: #333;">{threadInfo.Thread.Title}</a>
|
||||
<tr>
|
||||
<td>
|
||||
<div>
|
||||
<a href="/forum/{threadInfo.Thread.ID}">{threadInfo.Thread.Title}</a>
|
||||
</div>
|
||||
<div style="font-size: 12px; color: #666;">
|
||||
by {threadInfo.Author.Username} • {threadInfo.PostedTime}
|
||||
<div>
|
||||
by {threadInfo.Author.Username} • {threadInfo.Thread.PostedTime().Format("Jan 2, 2006 3:04 PM")}
|
||||
</div>
|
||||
</td>
|
||||
<td style="padding: 10px; text-align: center;">
|
||||
<td>
|
||||
{threadInfo.Thread.Replies}
|
||||
</td>
|
||||
<td style="padding: 10px; font-size: 12px; color: #666;">
|
||||
<td>
|
||||
{if threadInfo.LastReplyBy}
|
||||
by {threadInfo.LastReplyBy.Username}<br>
|
||||
{threadInfo.LastPostTime}
|
||||
{threadInfo.Thread.LastPostTime().Format("Jan 2, 2006 3:04 PM")}
|
||||
{else}
|
||||
No replies
|
||||
{/if}
|
||||
@ -45,12 +45,12 @@
|
||||
</table>
|
||||
|
||||
{if totalPages > 1}
|
||||
<div style="margin-top: 20px; text-align: center;">
|
||||
<div>
|
||||
{if hasPrev}
|
||||
<a href="/forum?page={currentPage - 1}"><button class="btn">Previous</button></a>
|
||||
{/if}
|
||||
|
||||
<span style="margin: 0 10px;">Page {currentPage} of {totalPages}</span>
|
||||
<span>Page {currentPage} of {totalPages}</span>
|
||||
|
||||
{if hasNext}
|
||||
<a href="/forum?page={currentPage + 1}"><button class="btn">Next</button></a>
|
||||
@ -59,7 +59,7 @@
|
||||
{/if}
|
||||
|
||||
{else}
|
||||
<div style="text-align: center; padding: 40px; color: #666;">
|
||||
<div>
|
||||
No threads yet. <a href="/forum/new">Create the first one!</a>
|
||||
</div>
|
||||
{/if}
|
||||
|
@ -10,20 +10,20 @@
|
||||
</div>
|
||||
|
||||
<!-- Original Thread Post -->
|
||||
<div style="display: flex; border: 1px solid #ddd; margin-bottom: 20px; background-color: #fafafa;">
|
||||
<div style="width: 150px; padding: 15px; background-color: #f5f5f5; border-right: 1px solid #ddd;">
|
||||
<div style="font-weight: bold; color: #333;">{threadAuthor.Username}</div>
|
||||
<div style="font-size: 12px; color: #666; margin-top: 5px;">
|
||||
<div>
|
||||
<div>
|
||||
<div>{threadAuthor.Username}</div>
|
||||
<div>
|
||||
Level {threadAuthor.Level}<br>
|
||||
{threadAuthorClass}
|
||||
{threadAuthor.Class().Name}
|
||||
</div>
|
||||
</div>
|
||||
<div style="flex: 1; padding: 15px;">
|
||||
<div style="margin-bottom: 10px;">
|
||||
{threadContent}
|
||||
<div>
|
||||
<div>
|
||||
{md(thread.Content)}
|
||||
</div>
|
||||
<div style="font-size: 11px; color: #888; border-top: 1px solid #eee; padding-top: 10px; margin-top: 10px;">
|
||||
Posted: {threadPostedTime}
|
||||
<div>
|
||||
Posted: {thread.PostedTime().Format("Jan 2, 2006 3:04 PM")}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -31,20 +31,20 @@
|
||||
<!-- Replies -->
|
||||
{if #replyInfos > 0}
|
||||
{for replyInfo in replyInfos}
|
||||
<div style="display: flex; border: 1px solid #ddd; margin-bottom: 10px;">
|
||||
<div style="width: 150px; padding: 15px; background-color: #f9f9f9; border-right: 1px solid #ddd;">
|
||||
<div style="font-weight: bold; color: #333;">{replyInfo.Author.Username}</div>
|
||||
<div style="font-size: 12px; color: #666; margin-top: 5px;">
|
||||
<div>
|
||||
<div>
|
||||
<div>{replyInfo.Author.Username}</div>
|
||||
<div>
|
||||
Level {replyInfo.Author.Level}<br>
|
||||
{replyInfo.AuthorClass}
|
||||
{replyInfo.Author.Class().Name}
|
||||
</div>
|
||||
</div>
|
||||
<div style="flex: 1; padding: 15px;">
|
||||
<div style="margin-bottom: 10px;">
|
||||
{replyInfo.Content}
|
||||
<div>
|
||||
<div>
|
||||
{markdown(replyInfo.Post.Content)}
|
||||
</div>
|
||||
<div style="font-size: 11px; color: #888; border-top: 1px solid #eee; padding-top: 10px; margin-top: 10px;">
|
||||
Posted: {replyInfo.PostedTime}
|
||||
<div>
|
||||
Posted: {replyInfo.Post.PostedTime().Format("Jan 2, 2006 3:04 PM")}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -53,12 +53,12 @@
|
||||
|
||||
<!-- Pagination -->
|
||||
{if totalPages > 1}
|
||||
<div style="margin-top: 20px; text-align: center;">
|
||||
<div>
|
||||
{if hasPrev}
|
||||
<a href="/forum/{thread.ID}?page={currentPage - 1}"><button class="btn">Previous</button></a>
|
||||
{/if}
|
||||
|
||||
<span style="margin: 0 10px;">Page {currentPage} of {totalPages}</span>
|
||||
<span>Page {currentPage} of {totalPages}</span>
|
||||
|
||||
{if hasNext}
|
||||
<a href="/forum/{thread.ID}?page={currentPage + 1}"><button class="btn">Next</button></a>
|
||||
@ -67,7 +67,7 @@
|
||||
{/if}
|
||||
|
||||
<!-- Reply Button at Bottom -->
|
||||
<div style="margin-top: 20px; text-align: center;">
|
||||
<div>
|
||||
<a href="/forum/{thread.ID}/reply"><button class="btn btn-primary">Reply to Thread</button></a>
|
||||
</div>
|
||||
{/block}
|
Loading…
x
Reference in New Issue
Block a user