Implement post links in feed / landing page
This commit is contained in:
parent
4f9df3382f
commit
5d697e5fff
|
@ -0,0 +1,41 @@
|
|||
package migrations
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"git.handmade.network/hmn/hmn/src/migration/types"
|
||||
"git.handmade.network/hmn/hmn/src/oops"
|
||||
"github.com/jackc/pgx/v4"
|
||||
)
|
||||
|
||||
func init() {
|
||||
registerMigration(RenameHMNProject{})
|
||||
}
|
||||
|
||||
type RenameHMNProject struct{}
|
||||
|
||||
func (m RenameHMNProject) Version() types.MigrationVersion {
|
||||
return types.MigrationVersion(time.Date(2021, 4, 28, 1, 55, 37, 0, time.UTC))
|
||||
}
|
||||
|
||||
func (m RenameHMNProject) Name() string {
|
||||
return "RenameHMNProject"
|
||||
}
|
||||
|
||||
func (m RenameHMNProject) Description() string {
|
||||
return "Rename the special HMN project"
|
||||
}
|
||||
|
||||
func (m RenameHMNProject) Up(ctx context.Context, tx pgx.Tx) error {
|
||||
_, err := tx.Exec(ctx, `UPDATE handmade_project SET name = 'Handmade Network' WHERE id = 1`)
|
||||
if err != nil {
|
||||
return oops.New(err, "failed to rename project")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m RenameHMNProject) Down(ctx context.Context, tx pgx.Tx) error {
|
||||
panic("Implement me")
|
||||
}
|
|
@ -4,19 +4,18 @@ import (
|
|||
"context"
|
||||
|
||||
"git.handmade.network/hmn/hmn/src/db"
|
||||
"git.handmade.network/hmn/hmn/src/hmnurl"
|
||||
"github.com/jackc/pgx/v4/pgxpool"
|
||||
)
|
||||
|
||||
type CategoryType int
|
||||
type CategoryKind int
|
||||
|
||||
const (
|
||||
CatTypeBlog CategoryType = iota + 1
|
||||
CatTypeForum
|
||||
CatTypeStatic
|
||||
CatTypeAnnotation
|
||||
CatTypeWiki
|
||||
CatTypeLibraryResource
|
||||
CatKindBlog CategoryKind = iota + 1
|
||||
CatKindForum
|
||||
CatKindStatic
|
||||
CatKindAnnotation
|
||||
CatKindWiki
|
||||
CatKindLibraryResource
|
||||
)
|
||||
|
||||
type Category struct {
|
||||
|
@ -28,7 +27,7 @@ type Category struct {
|
|||
Slug *string `db:"slug"` // TODO: Make not null
|
||||
Name *string `db:"name"` // TODO: Make not null
|
||||
Blurb *string `db:"blurb"` // TODO: Make not null
|
||||
Kind CategoryType `db:"kind"`
|
||||
Kind CategoryKind `db:"kind"`
|
||||
Color1 string `db:"color_1"`
|
||||
Color2 string `db:"color_2"`
|
||||
Depth int `db:"depth"` // TODO: What is this?
|
||||
|
@ -72,51 +71,3 @@ func (c *Category) GetHierarchy(ctx context.Context, conn *pgxpool.Pool) []Categ
|
|||
|
||||
return result
|
||||
}
|
||||
|
||||
func GetCategoryUrls(ctx context.Context, conn *pgxpool.Pool, cats ...*Category) map[int]string {
|
||||
var projectIds []int
|
||||
for _, cat := range cats {
|
||||
id := *cat.ProjectID
|
||||
|
||||
alreadyInList := false
|
||||
for _, otherId := range projectIds {
|
||||
if otherId == id {
|
||||
alreadyInList = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !alreadyInList {
|
||||
projectIds = append(projectIds, id)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(inarray)!!!!!
|
||||
|
||||
//for _, cat := range cats {
|
||||
// hierarchy := makeCategoryUrl(cat.GetHierarchy(ctx, conn))
|
||||
//}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func makeCategoryUrl(cats []*Category, subdomain string) string {
|
||||
path := ""
|
||||
for i, cat := range cats {
|
||||
if i == 0 {
|
||||
switch cat.Kind {
|
||||
case CatTypeBlog:
|
||||
path += "/blogs"
|
||||
case CatTypeForum:
|
||||
path += "/forums"
|
||||
// TODO: All cat types?
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
} else {
|
||||
path += "/" + *cat.Slug
|
||||
}
|
||||
}
|
||||
|
||||
return hmnurl.ProjectUrl(path, nil, subdomain)
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@ type Post struct {
|
|||
CurrentID int `db:"current_id"`
|
||||
ProjectID int `db:"project_id"`
|
||||
|
||||
CategoryType CategoryType `db:"category_kind"`
|
||||
CategoryKind CategoryKind `db:"category_kind"`
|
||||
|
||||
Depth int `db:"depth"`
|
||||
Slug string `db:"slug"`
|
||||
|
|
|
@ -1,20 +0,0 @@
|
|||
package templates
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"git.handmade.network/hmn/hmn/src/hmnurl"
|
||||
"git.handmade.network/hmn/hmn/src/models"
|
||||
)
|
||||
|
||||
func PostUrl(post models.Post, catType models.CategoryType, subdomain string) string {
|
||||
switch catType {
|
||||
// TODO: All the relevant post types. Maybe it doesn't make sense to lump them all together here.
|
||||
case models.CatTypeBlog:
|
||||
return hmnurl.ProjectUrl(fmt.Sprintf("blogs/p/%d/e/%d", post.ThreadID, post.ID), nil, subdomain)
|
||||
case models.CatTypeForum:
|
||||
return hmnurl.ProjectUrl(fmt.Sprintf("forums/t/%d/p/%d", post.ThreadID, post.ID), nil, subdomain)
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
|
@ -7,6 +7,7 @@ import (
|
|||
"time"
|
||||
|
||||
"git.handmade.network/hmn/hmn/src/db"
|
||||
"git.handmade.network/hmn/hmn/src/hmnurl"
|
||||
"git.handmade.network/hmn/hmn/src/logging"
|
||||
"git.handmade.network/hmn/hmn/src/models"
|
||||
"git.handmade.network/hmn/hmn/src/oops"
|
||||
|
@ -30,14 +31,11 @@ func Feed(c *RequestContext) ResponseData {
|
|||
FROM
|
||||
handmade_post AS post
|
||||
WHERE
|
||||
post.category_kind IN ($1, $2, $3, $4)
|
||||
post.category_kind = ANY ($1)
|
||||
AND NOT moderated
|
||||
`,
|
||||
models.CatTypeForum,
|
||||
models.CatTypeBlog,
|
||||
models.CatTypeWiki,
|
||||
models.CatTypeLibraryResource,
|
||||
) // TODO(inarray)
|
||||
[]models.CategoryKind{models.CatKindForum, models.CatKindBlog, models.CatKindWiki, models.CatKindLibraryResource},
|
||||
)
|
||||
c.Perf.EndBlock()
|
||||
if err != nil {
|
||||
return ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to get count of feed posts"))
|
||||
|
@ -74,12 +72,13 @@ func Feed(c *RequestContext) ResponseData {
|
|||
|
||||
c.Perf.StartBlock("SQL", "Fetch posts")
|
||||
type feedPostQuery struct {
|
||||
Post models.Post `db:"post"`
|
||||
Thread models.Thread `db:"thread"`
|
||||
Proj models.Project `db:"proj"`
|
||||
User models.User `db:"auth_user"`
|
||||
ThreadLastReadTime *time.Time `db:"tlri.lastread"`
|
||||
CatLastReadTime *time.Time `db:"clri.lastread"`
|
||||
Post models.Post `db:"post"`
|
||||
Thread models.Thread `db:"thread"`
|
||||
Cat models.Category `db:"cat"`
|
||||
Proj models.Project `db:"proj"`
|
||||
User models.User `db:"auth_user"`
|
||||
ThreadLastReadTime *time.Time `db:"tlri.lastread"`
|
||||
CatLastReadTime *time.Time `db:"clri.lastread"`
|
||||
}
|
||||
posts, err := db.Query(c.Context(), c.Conn, feedPostQuery{},
|
||||
`
|
||||
|
@ -87,6 +86,7 @@ func Feed(c *RequestContext) ResponseData {
|
|||
FROM
|
||||
handmade_post AS post
|
||||
JOIN handmade_thread AS thread ON thread.id = post.thread_id
|
||||
JOIN handmade_category AS cat ON cat.id = post.category_id
|
||||
JOIN handmade_project AS proj ON proj.id = post.project_id
|
||||
LEFT OUTER JOIN handmade_threadlastreadinfo AS tlri ON (
|
||||
tlri.thread_id = post.thread_id
|
||||
|
@ -98,25 +98,24 @@ func Feed(c *RequestContext) ResponseData {
|
|||
)
|
||||
LEFT OUTER JOIN auth_user ON post.author_id = auth_user.id
|
||||
WHERE
|
||||
post.category_kind IN ($2, $3, $4, $5)
|
||||
post.category_kind = ANY ($2)
|
||||
AND post.moderated = FALSE
|
||||
AND post.thread_id IS NOT NULL
|
||||
ORDER BY postdate DESC
|
||||
LIMIT $6 OFFSET $7
|
||||
LIMIT $3 OFFSET $4
|
||||
`,
|
||||
currentUserId,
|
||||
models.CatTypeForum,
|
||||
models.CatTypeBlog,
|
||||
models.CatTypeWiki,
|
||||
models.CatTypeLibraryResource,
|
||||
[]models.CategoryKind{models.CatKindForum, models.CatKindBlog, models.CatKindWiki, models.CatKindLibraryResource},
|
||||
postsPerPage,
|
||||
howManyPostsToSkip,
|
||||
) // TODO(inarray)
|
||||
)
|
||||
c.Perf.EndBlock()
|
||||
if err != nil {
|
||||
return ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch feed posts"))
|
||||
}
|
||||
|
||||
categoryUrls := GetAllCategoryUrls(c.Context(), c.Conn)
|
||||
|
||||
var postItems []templates.PostListItem
|
||||
for _, iPostResult := range posts.ToSlice() {
|
||||
postResult := iPostResult.(*feedPostQuery)
|
||||
|
@ -128,25 +127,33 @@ func Feed(c *RequestContext) ResponseData {
|
|||
hasRead = true
|
||||
}
|
||||
|
||||
var parents []models.Category
|
||||
// parents := postResult.Cat.GetHierarchy(c.Context(), c.Conn)
|
||||
parents := postResult.Cat.GetHierarchy(c.Context(), c.Conn)
|
||||
logging.Debug().Interface("parents", parents).Msg("")
|
||||
|
||||
var breadcrumbs []templates.Breadcrumb
|
||||
breadcrumbs = append(breadcrumbs, templates.Breadcrumb{
|
||||
Name: *postResult.Proj.Name,
|
||||
Url: "nargle", // TODO
|
||||
Url: hmnurl.ProjectUrl("/", nil, postResult.Proj.Subdomain()),
|
||||
})
|
||||
for i := len(parents) - 1; i >= 0; i-- {
|
||||
for _, parent := range parents {
|
||||
name := *parent.Name
|
||||
if parent.ParentID == nil {
|
||||
switch parent.Kind {
|
||||
case models.CatKindForum:
|
||||
name = "Forums"
|
||||
case models.CatKindBlog:
|
||||
name = "Blog"
|
||||
}
|
||||
}
|
||||
breadcrumbs = append(breadcrumbs, templates.Breadcrumb{
|
||||
Name: *parents[i].Name,
|
||||
Url: "nargle", // TODO
|
||||
Name: name,
|
||||
Url: categoryUrls[parent.ID],
|
||||
})
|
||||
}
|
||||
|
||||
postItems = append(postItems, templates.PostListItem{
|
||||
Title: postResult.Thread.Title,
|
||||
Url: templates.PostUrl(postResult.Post, postResult.Post.CategoryType, postResult.Proj.Subdomain()),
|
||||
Url: PostUrl(postResult.Post, postResult.Post.CategoryKind, categoryUrls[postResult.Post.CategoryID]),
|
||||
User: templates.UserToTemplate(&postResult.User),
|
||||
Date: postResult.Post.PostDate,
|
||||
Breadcrumbs: breadcrumbs,
|
||||
|
|
|
@ -64,6 +64,8 @@ func Index(c *RequestContext) ResponseData {
|
|||
c.Perf.EndBlock()
|
||||
c.Logger.Info().Interface("allProjects", allProjects).Msg("all the projects")
|
||||
|
||||
categoryUrls := GetAllCategoryUrls(c.Context(), c.Conn)
|
||||
|
||||
var currentUserId *int
|
||||
if c.CurrentUser != nil {
|
||||
currentUserId = &c.CurrentUser.ID
|
||||
|
@ -105,7 +107,7 @@ func Index(c *RequestContext) ResponseData {
|
|||
`,
|
||||
currentUserId,
|
||||
proj.ID,
|
||||
models.CatTypeBlog, models.CatTypeForum, models.CatTypeWiki, models.CatTypeLibraryResource,
|
||||
models.CatKindBlog, models.CatKindForum, models.CatKindWiki, models.CatKindLibraryResource,
|
||||
maxPosts,
|
||||
)
|
||||
c.Perf.EndBlock()
|
||||
|
@ -130,7 +132,7 @@ func Index(c *RequestContext) ResponseData {
|
|||
}
|
||||
|
||||
featurable := (!proj.IsHMN() &&
|
||||
projectPost.Post.CategoryType == models.CatTypeBlog &&
|
||||
projectPost.Post.CategoryKind == models.CatKindBlog &&
|
||||
projectPost.Post.ParentID == nil &&
|
||||
landingPageProject.FeaturedPost == nil)
|
||||
|
||||
|
@ -156,7 +158,7 @@ func Index(c *RequestContext) ResponseData {
|
|||
|
||||
landingPageProject.FeaturedPost = &LandingPageFeaturedPost{
|
||||
Title: projectPost.Thread.Title,
|
||||
Url: templates.PostUrl(projectPost.Post, projectPost.Post.CategoryType, proj.Subdomain()),
|
||||
Url: PostUrl(projectPost.Post, projectPost.Post.CategoryKind, categoryUrls[projectPost.Post.CategoryID]),
|
||||
User: templates.UserToTemplate(&projectPost.User),
|
||||
Date: projectPost.Post.PostDate,
|
||||
Unread: !hasRead,
|
||||
|
@ -165,7 +167,7 @@ func Index(c *RequestContext) ResponseData {
|
|||
} else {
|
||||
landingPageProject.Posts = append(landingPageProject.Posts, templates.PostListItem{
|
||||
Title: projectPost.Thread.Title,
|
||||
Url: templates.PostUrl(projectPost.Post, projectPost.Post.CategoryType, proj.Subdomain()),
|
||||
Url: PostUrl(projectPost.Post, projectPost.Post.CategoryKind, categoryUrls[projectPost.Post.CategoryID]),
|
||||
User: templates.UserToTemplate(&projectPost.User),
|
||||
Date: projectPost.Post.PostDate,
|
||||
Unread: !hasRead,
|
||||
|
@ -198,7 +200,7 @@ func Index(c *RequestContext) ResponseData {
|
|||
AND cat.kind = $2
|
||||
`,
|
||||
models.HMNProjectID,
|
||||
models.CatTypeBlog,
|
||||
models.CatKindBlog,
|
||||
)
|
||||
if err != nil {
|
||||
return ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch latest news post"))
|
||||
|
@ -261,7 +263,7 @@ func Index(c *RequestContext) ResponseData {
|
|||
LIMIT 1
|
||||
`,
|
||||
models.HMNProjectID,
|
||||
models.CatTypeBlog,
|
||||
models.CatKindBlog,
|
||||
)
|
||||
if err != nil {
|
||||
return ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch news post"))
|
||||
|
@ -272,11 +274,11 @@ func Index(c *RequestContext) ResponseData {
|
|||
baseData.BodyClasses = append(baseData.BodyClasses, "hmdev", "landing") // TODO: Is "hmdev" necessary any more?
|
||||
|
||||
var res ResponseData
|
||||
err = res.WriteTemplate("index.html", LandingTemplateData{
|
||||
err = res.WriteTemplate("landing.html", LandingTemplateData{
|
||||
BaseData: baseData,
|
||||
NewsPost: LandingPageFeaturedPost{
|
||||
Title: newsPostResult.Thread.Title,
|
||||
Url: templates.PostUrl(newsPostResult.Post, models.CatTypeBlog, ""),
|
||||
Url: PostUrl(newsPostResult.Post, models.CatKindBlog, ""),
|
||||
User: templates.UserToTemplate(&newsPostResult.User),
|
||||
Date: newsPostResult.Post.PostDate,
|
||||
Unread: true, // TODO
|
||||
|
|
|
@ -0,0 +1,126 @@
|
|||
package website
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"git.handmade.network/hmn/hmn/src/db"
|
||||
"git.handmade.network/hmn/hmn/src/hmnurl"
|
||||
"git.handmade.network/hmn/hmn/src/models"
|
||||
"github.com/jackc/pgx/v4/pgxpool"
|
||||
)
|
||||
|
||||
type categoryUrlQueryResult struct {
|
||||
Cat models.Category `db:"cat"`
|
||||
Project models.Project `db:"project"`
|
||||
}
|
||||
|
||||
func GetAllCategoryUrls(ctx context.Context, conn *pgxpool.Pool) map[int]string {
|
||||
it, err := db.Query(ctx, conn, categoryUrlQueryResult{},
|
||||
`
|
||||
SELECT $columns
|
||||
FROM
|
||||
handmade_category AS cat
|
||||
JOIN handmade_project AS project ON project.id = cat.project_id
|
||||
`,
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer it.Close()
|
||||
|
||||
return makeCategoryUrls(it.ToSlice())
|
||||
}
|
||||
|
||||
func GetProjectCategoryUrls(ctx context.Context, conn *pgxpool.Pool, projectId ...int) map[int]string {
|
||||
it, err := db.Query(ctx, conn, categoryUrlQueryResult{},
|
||||
`
|
||||
SELECT $columns
|
||||
FROM
|
||||
handmade_category AS cat
|
||||
JOIN handmade_project AS project ON project.id = cat.project_id
|
||||
WHERE
|
||||
project.id = ANY ($1)
|
||||
`,
|
||||
projectId,
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer it.Close()
|
||||
|
||||
return makeCategoryUrls(it.ToSlice())
|
||||
}
|
||||
|
||||
func makeCategoryUrls(rows []interface{}) map[int]string {
|
||||
categories := make(map[int]*models.Category)
|
||||
for _, irow := range rows {
|
||||
cat := irow.(*categoryUrlQueryResult).Cat
|
||||
categories[cat.ID] = &cat
|
||||
}
|
||||
|
||||
result := make(map[int]string)
|
||||
for _, irow := range rows {
|
||||
row := irow.(*categoryUrlQueryResult)
|
||||
|
||||
// get hierarchy (backwards, so current -> parent -> root)
|
||||
var hierarchyReverse []*models.Category
|
||||
currentCatID := row.Cat.ID
|
||||
for {
|
||||
cat := categories[currentCatID]
|
||||
|
||||
hierarchyReverse = append(hierarchyReverse, cat)
|
||||
if cat.ParentID == nil {
|
||||
break
|
||||
} else {
|
||||
currentCatID = *cat.ParentID
|
||||
}
|
||||
}
|
||||
|
||||
// reverse to get root -> parent -> current
|
||||
hierarchy := make([]*models.Category, len(hierarchyReverse))
|
||||
for i := len(hierarchyReverse) - 1; i >= 0; i-- {
|
||||
hierarchy[len(hierarchyReverse)-1-i] = hierarchyReverse[i]
|
||||
}
|
||||
|
||||
result[row.Cat.ID] = CategoryUrl(row.Project.Subdomain(), hierarchy...)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func CategoryUrl(subdomain string, cats ...*models.Category) string {
|
||||
path := ""
|
||||
for i, cat := range cats {
|
||||
if i == 0 {
|
||||
switch cat.Kind {
|
||||
case models.CatKindBlog:
|
||||
path += "/blogs"
|
||||
case models.CatKindForum:
|
||||
path += "/forums"
|
||||
// TODO: All cat types?
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
} else {
|
||||
path += "/" + *cat.Slug
|
||||
}
|
||||
}
|
||||
|
||||
return hmnurl.ProjectUrl(path, nil, subdomain)
|
||||
}
|
||||
|
||||
func PostUrl(post models.Post, catKind models.CategoryKind, categoryUrl string) string {
|
||||
categoryUrl = strings.TrimRight(categoryUrl, "/")
|
||||
|
||||
switch catKind {
|
||||
// TODO: All the relevant post types. Maybe it doesn't make sense to lump them all together here.
|
||||
case models.CatKindBlog:
|
||||
return fmt.Sprintf("%s/p/%d/e/%d", categoryUrl, post.ThreadID, post.ID)
|
||||
case models.CatKindForum:
|
||||
return fmt.Sprintf("%s/t/%d/p/%d", categoryUrl, post.ThreadID, post.ID)
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
Loading…
Reference in New Issue