Render subcategories of forum categories
This commit is contained in:
parent
a0155bfc5e
commit
b217cd5592
|
@ -2,21 +2,46 @@
|
||||||
|
|
||||||
{{ define "content" }}
|
{{ define "content" }}
|
||||||
<div class="content-block">
|
<div class="content-block">
|
||||||
<div class="optionbar">
|
{{ range .Subcategories }}
|
||||||
<div class="options">
|
<div class="pv3">
|
||||||
{{ if .User }}
|
<h2 class="ma0 ph3 pb2">
|
||||||
<a class="button new-thread" href="{{ printf "%s/t/new" .CategoryUrl }}"><span class="big">+</span> New Thread</a>
|
<a href="{{ .Url }}">
|
||||||
<a class="button" href="{{ printf "%s/markread" .CategoryUrl }}"><span class="big">✓</span> Mark threads here as read</a>
|
{{ .Name }} »<br/>
|
||||||
{{ else }}
|
</a>
|
||||||
<a class="button" href="{% url 'member_login' subdomain=request.subdomain %}">Log in to post a new thread</a>
|
</h2>
|
||||||
|
{{ range .Threads }}
|
||||||
|
{{ template "thread_list_item.html" . }}
|
||||||
|
{{ end }}
|
||||||
|
{{ $more := sub .TotalThreads 3 }}
|
||||||
|
{{ if gt $more 0 }}
|
||||||
|
<div class="ph3 pv1">
|
||||||
|
<a class="title" href="{{ .Url }}">{{ $more }} more »</a>
|
||||||
|
</div>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
</div>
|
</div>
|
||||||
<div class="options">
|
{{ end }}
|
||||||
{{ template "pagination.html" .Pagination }}
|
<div class="optionbar">
|
||||||
</div>
|
{{ template "forum_category_options" . }}
|
||||||
</div>
|
</div>
|
||||||
{{ range .Threads }}
|
{{ range .Threads }}
|
||||||
{{ template "thread_list_item.html" . }}
|
{{ template "thread_list_item.html" . }}
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
<div class="optionbar bottom">
|
||||||
|
{{ template "forum_category_options" . }}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
||||||
|
{{ define "forum_category_options" }}
|
||||||
|
<div class="options">
|
||||||
|
{{ if .User }}
|
||||||
|
<a class="button new-thread" href="{{ printf "%s/t/new" .CategoryUrl }}"><span class="big">+</span> New Thread</a>
|
||||||
|
<a class="button" href="{{ printf "%s/markread" .CategoryUrl }}"><span class="big">✓</span> Mark threads here as read</a>
|
||||||
|
{{ else }}
|
||||||
|
<a class="button" href="{% url 'member_login' subdomain=request.subdomain %}">Log in to post a new thread</a>
|
||||||
|
{{ end }}
|
||||||
|
</div>
|
||||||
|
<div class="options">
|
||||||
|
{{ template "pagination.html" .Pagination }}
|
||||||
|
</div>
|
||||||
|
{{ end }}
|
|
@ -9,22 +9,28 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"git.handmade.network/hmn/hmn/src/hmnurl"
|
|
||||||
"git.handmade.network/hmn/hmn/src/templates"
|
|
||||||
|
|
||||||
"github.com/jackc/pgx/v4/pgxpool"
|
|
||||||
|
|
||||||
"git.handmade.network/hmn/hmn/src/db"
|
"git.handmade.network/hmn/hmn/src/db"
|
||||||
|
"git.handmade.network/hmn/hmn/src/hmnurl"
|
||||||
"git.handmade.network/hmn/hmn/src/models"
|
"git.handmade.network/hmn/hmn/src/models"
|
||||||
"git.handmade.network/hmn/hmn/src/oops"
|
"git.handmade.network/hmn/hmn/src/oops"
|
||||||
|
"git.handmade.network/hmn/hmn/src/templates"
|
||||||
|
"github.com/jackc/pgx/v4/pgxpool"
|
||||||
)
|
)
|
||||||
|
|
||||||
type forumCategoryData struct {
|
type forumCategoryData struct {
|
||||||
templates.BaseData
|
templates.BaseData
|
||||||
|
|
||||||
CategoryUrl string
|
CategoryUrl string
|
||||||
Threads []templates.ThreadListItem
|
Threads []templates.ThreadListItem
|
||||||
Pagination templates.Pagination
|
Pagination templates.Pagination
|
||||||
|
Subcategories []forumSubcategoryData
|
||||||
|
}
|
||||||
|
|
||||||
|
type forumSubcategoryData struct {
|
||||||
|
Name string
|
||||||
|
Url string
|
||||||
|
Threads []templates.ThreadListItem
|
||||||
|
TotalThreads int
|
||||||
}
|
}
|
||||||
|
|
||||||
func ForumCategory(c *RequestContext) ResponseData {
|
func ForumCategory(c *RequestContext) ResponseData {
|
||||||
|
@ -35,6 +41,7 @@ func ForumCategory(c *RequestContext) ResponseData {
|
||||||
currentCatId := fetchCatIdFromSlugs(c.Context(), c.Conn, catSlugs, c.CurrentProject.ID)
|
currentCatId := fetchCatIdFromSlugs(c.Context(), c.Conn, catSlugs, c.CurrentProject.ID)
|
||||||
categoryUrls := GetProjectCategoryUrls(c.Context(), c.Conn, c.CurrentProject.ID)
|
categoryUrls := GetProjectCategoryUrls(c.Context(), c.Conn, c.CurrentProject.ID)
|
||||||
|
|
||||||
|
c.Perf.StartBlock("SQL", "Fetch count of page threads")
|
||||||
numThreads, err := db.QueryInt(c.Context(), c.Conn,
|
numThreads, err := db.QueryInt(c.Context(), c.Conn,
|
||||||
`
|
`
|
||||||
SELECT COUNT(*)
|
SELECT COUNT(*)
|
||||||
|
@ -48,6 +55,7 @@ func ForumCategory(c *RequestContext) ResponseData {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(oops.New(err, "failed to get count of threads"))
|
panic(oops.New(err, "failed to get count of threads"))
|
||||||
}
|
}
|
||||||
|
c.Perf.EndBlock()
|
||||||
|
|
||||||
numPages := int(math.Ceil(float64(numThreads) / threadsPerPage))
|
numPages := int(math.Ceil(float64(numThreads) / threadsPerPage))
|
||||||
|
|
||||||
|
@ -71,7 +79,8 @@ func ForumCategory(c *RequestContext) ResponseData {
|
||||||
currentUserId = &c.CurrentUser.ID
|
currentUserId = &c.CurrentUser.ID
|
||||||
}
|
}
|
||||||
|
|
||||||
type mainPostsQueryResult struct {
|
c.Perf.StartBlock("SQL", "Fetch page threads")
|
||||||
|
type threadQueryResult struct {
|
||||||
Thread models.Thread `db:"thread"`
|
Thread models.Thread `db:"thread"`
|
||||||
FirstPost models.Post `db:"firstpost"`
|
FirstPost models.Post `db:"firstpost"`
|
||||||
LastPost models.Post `db:"lastpost"`
|
LastPost models.Post `db:"lastpost"`
|
||||||
|
@ -80,7 +89,7 @@ func ForumCategory(c *RequestContext) ResponseData {
|
||||||
ThreadLastReadTime *time.Time `db:"tlri.lastread"`
|
ThreadLastReadTime *time.Time `db:"tlri.lastread"`
|
||||||
CatLastReadTime *time.Time `db:"clri.lastread"`
|
CatLastReadTime *time.Time `db:"clri.lastread"`
|
||||||
}
|
}
|
||||||
itMainThreads, err := db.Query(c.Context(), c.Conn, mainPostsQueryResult{},
|
itMainThreads, err := db.Query(c.Context(), c.Conn, threadQueryResult{},
|
||||||
`
|
`
|
||||||
SELECT $columns
|
SELECT $columns
|
||||||
FROM
|
FROM
|
||||||
|
@ -111,12 +120,10 @@ func ForumCategory(c *RequestContext) ResponseData {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(oops.New(err, "failed to fetch threads"))
|
panic(oops.New(err, "failed to fetch threads"))
|
||||||
}
|
}
|
||||||
|
c.Perf.EndBlock()
|
||||||
defer itMainThreads.Close()
|
defer itMainThreads.Close()
|
||||||
|
|
||||||
var threads []templates.ThreadListItem
|
makeThreadListItem := func(row *threadQueryResult) templates.ThreadListItem {
|
||||||
for _, irow := range itMainThreads.ToSlice() {
|
|
||||||
row := irow.(*mainPostsQueryResult)
|
|
||||||
|
|
||||||
hasRead := false
|
hasRead := false
|
||||||
if row.ThreadLastReadTime != nil && row.ThreadLastReadTime.After(row.LastPost.PostDate) {
|
if row.ThreadLastReadTime != nil && row.ThreadLastReadTime.After(row.LastPost.PostDate) {
|
||||||
hasRead = true
|
hasRead = true
|
||||||
|
@ -124,7 +131,7 @@ func ForumCategory(c *RequestContext) ResponseData {
|
||||||
hasRead = true
|
hasRead = true
|
||||||
}
|
}
|
||||||
|
|
||||||
threads = append(threads, templates.ThreadListItem{
|
return templates.ThreadListItem{
|
||||||
Title: row.Thread.Title,
|
Title: row.Thread.Title,
|
||||||
Url: ThreadUrl(row.Thread, models.CatKindForum, categoryUrls[currentCatId]),
|
Url: ThreadUrl(row.Thread, models.CatKindForum, categoryUrls[currentCatId]),
|
||||||
|
|
||||||
|
@ -134,45 +141,111 @@ func ForumCategory(c *RequestContext) ResponseData {
|
||||||
LastDate: row.LastPost.PostDate,
|
LastDate: row.LastPost.PostDate,
|
||||||
|
|
||||||
Unread: !hasRead,
|
Unread: !hasRead,
|
||||||
})
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var threads []templates.ThreadListItem
|
||||||
|
for _, irow := range itMainThreads.ToSlice() {
|
||||||
|
row := irow.(*threadQueryResult)
|
||||||
|
threads = append(threads, makeThreadListItem(row))
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------
|
// ---------------------
|
||||||
// Subcategory things
|
// Subcategory things
|
||||||
// ---------------------
|
// ---------------------
|
||||||
|
|
||||||
//c.Perf.StartBlock("SQL", "Fetch subcategories")
|
var subcats []forumSubcategoryData
|
||||||
//type queryResult struct {
|
if page == 1 {
|
||||||
// Cat models.Category `db:"cat"`
|
c.Perf.StartBlock("SQL", "Fetch subcategories")
|
||||||
//}
|
type subcatQueryResult struct {
|
||||||
//itSubcats, err := db.Query(c.Context(), c.Conn, queryResult{},
|
Cat models.Category `db:"cat"`
|
||||||
// `
|
}
|
||||||
// WITH current AS (
|
itSubcats, err := db.Query(c.Context(), c.Conn, subcatQueryResult{},
|
||||||
// SELECT id
|
`
|
||||||
// FROM handmade_category
|
SELECT $columns
|
||||||
// WHERE
|
FROM
|
||||||
// slug = $1
|
handmade_category AS cat
|
||||||
// AND kind = $2
|
WHERE
|
||||||
// AND project_id = $3
|
cat.parent_id = $1
|
||||||
// )
|
`,
|
||||||
// SELECT $columns
|
currentCatId,
|
||||||
// FROM
|
)
|
||||||
// handmade_category AS cat,
|
if err != nil {
|
||||||
// current
|
panic(oops.New(err, "failed to fetch subcategories"))
|
||||||
// WHERE
|
}
|
||||||
// cat.id = current.id
|
defer itSubcats.Close()
|
||||||
// OR cat.parent_id = current.id
|
c.Perf.EndBlock()
|
||||||
// `,
|
|
||||||
// catSlug,
|
|
||||||
// models.CatKindForum,
|
|
||||||
// c.CurrentProject.ID,
|
|
||||||
//)
|
|
||||||
//if err != nil {
|
|
||||||
// panic(oops.New(err, "failed to fetch subcategories"))
|
|
||||||
//}
|
|
||||||
//c.Perf.EndBlock()
|
|
||||||
|
|
||||||
//_ = itSubcats // TODO: Actually query subcategory post data
|
for _, irow := range itSubcats.ToSlice() {
|
||||||
|
catRow := irow.(*subcatQueryResult)
|
||||||
|
|
||||||
|
c.Perf.StartBlock("SQL", "Fetch count of subcategory threads")
|
||||||
|
numThreads, err := db.QueryInt(c.Context(), c.Conn,
|
||||||
|
`
|
||||||
|
SELECT COUNT(*)
|
||||||
|
FROM handmade_thread AS thread
|
||||||
|
WHERE
|
||||||
|
thread.category_id = $1
|
||||||
|
AND NOT thread.deleted
|
||||||
|
`,
|
||||||
|
catRow.Cat.ID,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
panic(oops.New(err, "failed to get count of threads"))
|
||||||
|
}
|
||||||
|
c.Perf.EndBlock()
|
||||||
|
|
||||||
|
c.Perf.StartBlock("SQL", "Fetch subcategory threads")
|
||||||
|
itThreads, err := db.Query(c.Context(), c.Conn, threadQueryResult{},
|
||||||
|
`
|
||||||
|
SELECT $columns
|
||||||
|
FROM
|
||||||
|
handmade_thread AS thread
|
||||||
|
JOIN handmade_post AS firstpost ON thread.first_id = firstpost.id
|
||||||
|
JOIN handmade_post AS lastpost ON thread.last_id = lastpost.id
|
||||||
|
LEFT JOIN auth_user AS firstuser ON firstpost.author_id = firstuser.id
|
||||||
|
LEFT JOIN auth_user AS lastuser ON lastpost.author_id = lastuser.id
|
||||||
|
LEFT JOIN handmade_threadlastreadinfo AS tlri ON (
|
||||||
|
tlri.thread_id = thread.id
|
||||||
|
AND tlri.user_id = $2
|
||||||
|
)
|
||||||
|
LEFT JOIN handmade_categorylastreadinfo AS clri ON (
|
||||||
|
clri.category_id = $1
|
||||||
|
AND clri.user_id = $2
|
||||||
|
)
|
||||||
|
WHERE
|
||||||
|
thread.category_id = $1
|
||||||
|
AND NOT thread.deleted
|
||||||
|
ORDER BY lastpost.postdate DESC
|
||||||
|
LIMIT 3
|
||||||
|
`,
|
||||||
|
catRow.Cat.ID,
|
||||||
|
currentUserId,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
defer itThreads.Close()
|
||||||
|
c.Perf.EndBlock()
|
||||||
|
|
||||||
|
var threads []templates.ThreadListItem
|
||||||
|
for _, irow := range itThreads.ToSlice() {
|
||||||
|
threadRow := irow.(*threadQueryResult)
|
||||||
|
threads = append(threads, makeThreadListItem(threadRow))
|
||||||
|
}
|
||||||
|
|
||||||
|
subcats = append(subcats, forumSubcategoryData{
|
||||||
|
Name: *catRow.Cat.Name,
|
||||||
|
Url: categoryUrls[catRow.Cat.ID],
|
||||||
|
Threads: threads,
|
||||||
|
TotalThreads: numThreads,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------
|
||||||
|
// Template assembly
|
||||||
|
// ---------------------
|
||||||
|
|
||||||
baseData := getBaseData(c)
|
baseData := getBaseData(c)
|
||||||
baseData.Title = *c.CurrentProject.Name + " Forums"
|
baseData.Title = *c.CurrentProject.Name + " Forums"
|
||||||
|
@ -202,6 +275,7 @@ func ForumCategory(c *RequestContext) ResponseData {
|
||||||
NextUrl: fmt.Sprintf("%s/%d", categoryUrls[currentCatId], page+1),
|
NextUrl: fmt.Sprintf("%s/%d", categoryUrls[currentCatId], page+1),
|
||||||
PreviousUrl: fmt.Sprintf("%s/%d", categoryUrls[currentCatId], page-1),
|
PreviousUrl: fmt.Sprintf("%s/%d", categoryUrls[currentCatId], page-1),
|
||||||
},
|
},
|
||||||
|
Subcategories: subcats,
|
||||||
}, c.Perf)
|
}, c.Perf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
|
|
|
@ -73,7 +73,7 @@ func NewWebsiteRoutes(conn *pgxpool.Pool, perfCollector *perf.PerfCollector) htt
|
||||||
})
|
})
|
||||||
mainRoutes.GET(`^/feed(/(?P<page>.+)?)?$`, Feed)
|
mainRoutes.GET(`^/feed(/(?P<page>.+)?)?$`, Feed)
|
||||||
|
|
||||||
mainRoutes.GET(`^/(?P<cats>forums(/[a-zA-Z]+?)*)(/(?P<page>\d+))?$`, ForumCategory)
|
mainRoutes.GET(`^/(?P<cats>forums(/[^\d]+?)*)(/(?P<page>\d+))?$`, ForumCategory)
|
||||||
// mainRoutes.GET(`^/(?P<cats>forums(/cat)*)/t/(?P<threadid>\d+)/p/(?P<postid>\d+)$`, ForumPost)
|
// mainRoutes.GET(`^/(?P<cats>forums(/cat)*)/t/(?P<threadid>\d+)/p/(?P<postid>\d+)$`, ForumPost)
|
||||||
|
|
||||||
mainRoutes.GET("^/assets/project.css$", ProjectCSS)
|
mainRoutes.GET("^/assets/project.css$", ProjectCSS)
|
||||||
|
|
Loading…
Reference in New Issue