Show project snippets on project pages
we need better filter UI, but do we really, though
This commit is contained in:
parent
39d11b549a
commit
df2942e84b
|
@ -18,16 +18,18 @@ func JamIndex(c *RequestContext) ResponseData {
|
||||||
daysUntil = 0
|
daysUntil = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
var tagIds []int
|
tagId := -1
|
||||||
jamTag, err := FetchTag(c.Context(), c.Conn, "wheeljam")
|
jamTag, err := FetchTag(c.Context(), c.Conn, TagQuery{
|
||||||
|
Text: []string{"wheeljam"},
|
||||||
|
})
|
||||||
if err == nil {
|
if err == nil {
|
||||||
tagIds = []int{jamTag.ID}
|
tagId = jamTag.ID
|
||||||
} else {
|
} else {
|
||||||
c.Logger.Warn().Err(err).Msg("failed to fetch jam tag; will fetch all snippets as a result")
|
c.Logger.Warn().Err(err).Msg("failed to fetch jam tag; will fetch all snippets as a result")
|
||||||
}
|
}
|
||||||
|
|
||||||
snippets, err := FetchSnippets(c.Context(), c.Conn, c.CurrentUser, SnippetQuery{
|
snippets, err := FetchSnippets(c.Context(), c.Conn, c.CurrentUser, SnippetQuery{
|
||||||
Tags: tagIds,
|
Tags: []int{tagId},
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch jam snippets"))
|
return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch jam snippets"))
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"math"
|
"math"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"sort"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"git.handmade.network/hmn/hmn/src/db"
|
"git.handmade.network/hmn/hmn/src/db"
|
||||||
|
@ -241,25 +242,25 @@ func ProjectHomepage(c *RequestContext) ResponseData {
|
||||||
}
|
}
|
||||||
c.Perf.EndBlock()
|
c.Perf.EndBlock()
|
||||||
|
|
||||||
var projectHomepageData ProjectHomepageData
|
var templateData ProjectHomepageData
|
||||||
|
|
||||||
projectHomepageData.BaseData = getBaseData(c, c.CurrentProject.Name, nil)
|
templateData.BaseData = getBaseData(c, c.CurrentProject.Name, nil)
|
||||||
//if canEdit {
|
//if canEdit {
|
||||||
// // TODO: Move to project-specific navigation
|
// // TODO: Move to project-specific navigation
|
||||||
// // projectHomepageData.BaseData.Header.EditURL = hmnurl.BuildProjectEdit(project.Slug, "")
|
// // templateData.BaseData.Header.EditURL = hmnurl.BuildProjectEdit(project.Slug, "")
|
||||||
//}
|
//}
|
||||||
projectHomepageData.BaseData.OpenGraphItems = append(projectHomepageData.BaseData.OpenGraphItems, templates.OpenGraphItem{
|
templateData.BaseData.OpenGraphItems = append(templateData.BaseData.OpenGraphItems, templates.OpenGraphItem{
|
||||||
Property: "og:description",
|
Property: "og:description",
|
||||||
Value: c.CurrentProject.Blurb,
|
Value: c.CurrentProject.Blurb,
|
||||||
})
|
})
|
||||||
|
|
||||||
projectHomepageData.Project = templates.ProjectToTemplate(c.CurrentProject, c.UrlContext.BuildHomepage(), c.Theme)
|
templateData.Project = templates.ProjectToTemplate(c.CurrentProject, c.UrlContext.BuildHomepage(), c.Theme)
|
||||||
for _, owner := range owners {
|
for _, owner := range owners {
|
||||||
projectHomepageData.Owners = append(projectHomepageData.Owners, templates.UserToTemplate(owner, c.Theme))
|
templateData.Owners = append(templateData.Owners, templates.UserToTemplate(owner, c.Theme))
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.CurrentProject.Hidden {
|
if c.CurrentProject.Hidden {
|
||||||
projectHomepageData.BaseData.AddImmediateNotice(
|
templateData.BaseData.AddImmediateNotice(
|
||||||
"hidden",
|
"hidden",
|
||||||
"NOTICE: This project is hidden. It is currently visible only to owners and site admins.",
|
"NOTICE: This project is hidden. It is currently visible only to owners and site admins.",
|
||||||
)
|
)
|
||||||
|
@ -268,7 +269,7 @@ func ProjectHomepage(c *RequestContext) ResponseData {
|
||||||
if c.CurrentProject.Lifecycle != models.ProjectLifecycleActive {
|
if c.CurrentProject.Lifecycle != models.ProjectLifecycleActive {
|
||||||
switch c.CurrentProject.Lifecycle {
|
switch c.CurrentProject.Lifecycle {
|
||||||
case models.ProjectLifecycleUnapproved:
|
case models.ProjectLifecycleUnapproved:
|
||||||
projectHomepageData.BaseData.AddImmediateNotice(
|
templateData.BaseData.AddImmediateNotice(
|
||||||
"unapproved",
|
"unapproved",
|
||||||
fmt.Sprintf(
|
fmt.Sprintf(
|
||||||
"NOTICE: This project has not yet been submitted for approval. It is only visible to owners. Please <a href=\"%s\">submit it for approval</a> when the project content is ready for review.",
|
"NOTICE: This project has not yet been submitted for approval. It is only visible to owners. Please <a href=\"%s\">submit it for approval</a> when the project content is ready for review.",
|
||||||
|
@ -276,27 +277,27 @@ func ProjectHomepage(c *RequestContext) ResponseData {
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
case models.ProjectLifecycleApprovalRequired:
|
case models.ProjectLifecycleApprovalRequired:
|
||||||
projectHomepageData.BaseData.AddImmediateNotice(
|
templateData.BaseData.AddImmediateNotice(
|
||||||
"unapproved",
|
"unapproved",
|
||||||
"NOTICE: This project is awaiting approval. It is only visible to owners and site admins.",
|
"NOTICE: This project is awaiting approval. It is only visible to owners and site admins.",
|
||||||
)
|
)
|
||||||
case models.ProjectLifecycleHiatus:
|
case models.ProjectLifecycleHiatus:
|
||||||
projectHomepageData.BaseData.AddImmediateNotice(
|
templateData.BaseData.AddImmediateNotice(
|
||||||
"hiatus",
|
"hiatus",
|
||||||
"NOTICE: This project is on hiatus and may not update for a while.",
|
"NOTICE: This project is on hiatus and may not update for a while.",
|
||||||
)
|
)
|
||||||
case models.ProjectLifecycleDead:
|
case models.ProjectLifecycleDead:
|
||||||
projectHomepageData.BaseData.AddImmediateNotice(
|
templateData.BaseData.AddImmediateNotice(
|
||||||
"dead",
|
"dead",
|
||||||
"NOTICE: Site staff have marked this project as being dead. If you intend to revive it, please contact a member of the Handmade Network staff.",
|
"NOTICE: Site staff have marked this project as being dead. If you intend to revive it, please contact a member of the Handmade Network staff.",
|
||||||
)
|
)
|
||||||
case models.ProjectLifecycleLTSRequired:
|
case models.ProjectLifecycleLTSRequired:
|
||||||
projectHomepageData.BaseData.AddImmediateNotice(
|
templateData.BaseData.AddImmediateNotice(
|
||||||
"lts-reqd",
|
"lts-reqd",
|
||||||
"NOTICE: This project is awaiting approval for maintenance-mode status.",
|
"NOTICE: This project is awaiting approval for maintenance-mode status.",
|
||||||
)
|
)
|
||||||
case models.ProjectLifecycleLTS:
|
case models.ProjectLifecycleLTS:
|
||||||
projectHomepageData.BaseData.AddImmediateNotice(
|
templateData.BaseData.AddImmediateNotice(
|
||||||
"lts",
|
"lts",
|
||||||
"NOTICE: This project has reached a state of completion.",
|
"NOTICE: This project has reached a state of completion.",
|
||||||
)
|
)
|
||||||
|
@ -304,15 +305,15 @@ func ProjectHomepage(c *RequestContext) ResponseData {
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, screenshot := range screenshotQueryResult.ToSlice() {
|
for _, screenshot := range screenshotQueryResult.ToSlice() {
|
||||||
projectHomepageData.Screenshots = append(projectHomepageData.Screenshots, hmnurl.BuildUserFile(screenshot.(*screenshotQuery).Filename))
|
templateData.Screenshots = append(templateData.Screenshots, hmnurl.BuildUserFile(screenshot.(*screenshotQuery).Filename))
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, link := range projectLinkResult.ToSlice() {
|
for _, link := range projectLinkResult.ToSlice() {
|
||||||
projectHomepageData.ProjectLinks = append(projectHomepageData.ProjectLinks, templates.LinkToTemplate(&link.(*projectLinkQuery).Link))
|
templateData.ProjectLinks = append(templateData.ProjectLinks, templates.LinkToTemplate(&link.(*projectLinkQuery).Link))
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, post := range postQueryResult.ToSlice() {
|
for _, post := range postQueryResult.ToSlice() {
|
||||||
projectHomepageData.RecentActivity = append(projectHomepageData.RecentActivity, PostToTimelineItem(
|
templateData.RecentActivity = append(templateData.RecentActivity, PostToTimelineItem(
|
||||||
c.UrlContext,
|
c.UrlContext,
|
||||||
lineageBuilder,
|
lineageBuilder,
|
||||||
&post.(*postQuery).Post,
|
&post.(*postQuery).Post,
|
||||||
|
@ -322,8 +323,38 @@ func ProjectHomepage(c *RequestContext) ResponseData {
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tagId := -1
|
||||||
|
if c.CurrentProject.TagID != nil {
|
||||||
|
tagId = *c.CurrentProject.TagID
|
||||||
|
}
|
||||||
|
|
||||||
|
snippets, err := FetchSnippets(c.Context(), c.Conn, c.CurrentUser, SnippetQuery{
|
||||||
|
Tags: []int{tagId},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch project snippets"))
|
||||||
|
}
|
||||||
|
for _, s := range snippets {
|
||||||
|
item := SnippetToTimelineItem(
|
||||||
|
&s.Snippet,
|
||||||
|
s.Asset,
|
||||||
|
s.DiscordMessage,
|
||||||
|
s.Tags,
|
||||||
|
s.Owner,
|
||||||
|
c.Theme,
|
||||||
|
)
|
||||||
|
item.SmallInfo = true
|
||||||
|
templateData.RecentActivity = append(templateData.RecentActivity, item)
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Perf.StartBlock("PROFILE", "Sort timeline")
|
||||||
|
sort.Slice(templateData.RecentActivity, func(i, j int) bool {
|
||||||
|
return templateData.RecentActivity[j].Date.Before(templateData.RecentActivity[i].Date)
|
||||||
|
})
|
||||||
|
c.Perf.EndBlock()
|
||||||
|
|
||||||
var res ResponseData
|
var res ResponseData
|
||||||
err = res.WriteTemplate("project_homepage.html", projectHomepageData, c.Perf)
|
err = res.WriteTemplate("project_homepage.html", templateData, c.Perf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to render project homepage template"))
|
return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to render project homepage template"))
|
||||||
}
|
}
|
||||||
|
|
|
@ -153,19 +153,38 @@ func FetchSnippet(
|
||||||
return res[0], nil
|
return res[0], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func FetchTags(ctx context.Context, dbConn db.ConnOrTx, text []string) ([]*models.Tag, error) {
|
type TagQuery struct {
|
||||||
|
IDs []int
|
||||||
|
Text []string
|
||||||
|
|
||||||
|
Limit, Offset int
|
||||||
|
}
|
||||||
|
|
||||||
|
func FetchTags(ctx context.Context, dbConn db.ConnOrTx, q TagQuery) ([]*models.Tag, error) {
|
||||||
perf := ExtractPerf(ctx)
|
perf := ExtractPerf(ctx)
|
||||||
perf.StartBlock("SQL", "Fetch snippets")
|
perf.StartBlock("SQL", "Fetch snippets")
|
||||||
defer perf.EndBlock()
|
defer perf.EndBlock()
|
||||||
|
|
||||||
it, err := db.Query(ctx, dbConn, models.Tag{},
|
var qb db.QueryBuilder
|
||||||
|
qb.Add(
|
||||||
`
|
`
|
||||||
SELECT $columns
|
SELECT $columns
|
||||||
FROM tags
|
FROM tags
|
||||||
WHERE text = ANY ($1)
|
WHERE
|
||||||
|
TRUE
|
||||||
`,
|
`,
|
||||||
text,
|
|
||||||
)
|
)
|
||||||
|
if len(q.IDs) > 0 {
|
||||||
|
qb.Add(`AND id = ANY ($?)`, q.IDs)
|
||||||
|
}
|
||||||
|
if len(q.Text) > 0 {
|
||||||
|
qb.Add(`AND text = ANY ($?)`, q.Text)
|
||||||
|
}
|
||||||
|
if q.Limit > 0 {
|
||||||
|
qb.Add(`LIMIT $? OFFSET $?`, q.Limit, q.Offset)
|
||||||
|
}
|
||||||
|
|
||||||
|
it, err := db.Query(ctx, dbConn, models.Tag{}, qb.String(), qb.Args()...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, oops.New(err, "failed to fetch tags")
|
return nil, oops.New(err, "failed to fetch tags")
|
||||||
}
|
}
|
||||||
|
@ -180,8 +199,8 @@ func FetchTags(ctx context.Context, dbConn db.ConnOrTx, text []string) ([]*model
|
||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func FetchTag(ctx context.Context, dbConn db.ConnOrTx, text string) (*models.Tag, error) {
|
func FetchTag(ctx context.Context, dbConn db.ConnOrTx, q TagQuery) (*models.Tag, error) {
|
||||||
tags, err := FetchTags(ctx, dbConn, []string{text})
|
tags, err := FetchTags(ctx, dbConn, q)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue