diff --git a/src/hmndata/project_helper.go b/src/hmndata/project_helper.go index 049bd48f..0a4faa94 100644 --- a/src/hmndata/project_helper.go +++ b/src/hmndata/project_helper.go @@ -29,6 +29,7 @@ type ProjectsQuery struct { ProjectIDs []int // if empty, all projects Slugs []string // if empty, all projects OwnerIDs []int // if empty, all projects + JamSlugs []string // if empty, all projects // Ignored when using CountProjects Limit, Offset int // if empty, no pagination @@ -101,6 +102,14 @@ func FetchProjects( ) } + if len(q.JamSlugs) > 0 { + qb.Add( + ` + JOIN jam_project ON jam_project.project_id = project.id + `, + ) + } + // Filters (permissions are checked after the query, in Go) qb.Add(` WHERE @@ -130,6 +139,9 @@ func FetchProjects( if len(q.Slugs) > 0 { qb.Add(`AND (project.slug != '' AND project.slug = ANY ($?))`, q.Slugs) } + if len(q.JamSlugs) > 0 { + qb.Add(`AND (jam_project.jam_slug = ANY ($?) AND jam_project.participating = TRUE)`, q.JamSlugs) + } // Output if q.Limit > 0 { diff --git a/src/hmnurl/urls.go b/src/hmnurl/urls.go index 9212477b..0e13de4b 100644 --- a/src/hmnurl/urls.go +++ b/src/hmnurl/urls.go @@ -320,6 +320,12 @@ func BuildProjectNew() string { return Url("/p/new", nil) } +func BuildProjectNewJam() string { + defer CatchPanic() + + return Url("/p/new", []Q{Q{Name: "jam", Value: "1"}}) +} + var RegexPersonalProject = regexp.MustCompile("^/p/(?P[0-9]+)(/(?P[a-zA-Z0-9-]+))?") func BuildPersonalProject(id int, slug string) string { diff --git a/src/templates/src/wheeljam_2022_feed.html b/src/templates/src/wheeljam_2022_feed.html new file mode 100644 index 00000000..75cb5a27 --- /dev/null +++ b/src/templates/src/wheeljam_2022_feed.html @@ -0,0 +1,280 @@ +{{/* + This is a copy-paste from base.html because we want to preserve the unique + style of this page no matter what future changes we make to the base. +*/}} + + + + + + + + + + {{ if .CanonicalLink }}{{ end }} + {{ range .OpenGraphItems }} + {{ if .Property }} + + {{ else }} + + {{ end }} + {{ end }} + {{ if .Title }} + {{ .Title }} | Handmade Network + {{ else }} + Handmade Network + {{ end }} + + + + + + + + + + + + +
+
+ {{ template "header.html" . }} +
+ +
+

Wheel Reinvention Jam

+

August 15 - 21, 2O22

+
+ A one-week jam to change the status quo. +
+
+ + +
+
+

Projects

+
+ {{ range .JamProjects }} +
+ {{ template "project_card.html" projectcarddata . "" }} +
+ {{ end }} +
+
+
+ +
+

Recent activity

+
+ {{ range .TimelineItems }} + {{ template "timeline_item.html" . }} + {{ end }} +
+
+ +
+ {{ template "footer.html" . }} +
+
+ + + + diff --git a/src/templates/src/wheeljam_2022_index.html b/src/templates/src/wheeljam_2022_index.html index 478f7607..b093a0fd 100644 --- a/src/templates/src/wheeljam_2022_index.html +++ b/src/templates/src/wheeljam_2022_index.html @@ -26,6 +26,9 @@ {{ end }} + + + @@ -105,12 +108,12 @@ text-decoration: underline; } - #actions { + .actions { margin-top: 1.5rem; } - #actions a { - text-decoration: none; + .actions a { + text-decoration: none !important; line-height: 1.4; font-weight: 500; @@ -118,11 +121,11 @@ background-color:rgba(255, 255, 255, 0.1); } - #actions a:hover { + .actions a:hover { background-color: rgba(255, 255, 255, 0.2); } - #actions a:active { + .actions a:active { background-color: rgba(255, 255, 255, 0.15); } @@ -183,6 +186,11 @@ text-transform: uppercase; } + .showcase-item { + background-color: rgba(0, 0, 0, 0.2); + border-color: rgba(255, 255, 255, 0.5); + } + @media screen and (min-width: 30em) { /* not small styles */ @@ -208,11 +216,11 @@ margin-top: 1.2rem; } - #actions { + .actions { margin-top: 2.2rem; } - #actions a { + .actions a { font-size: 1.2rem; } @@ -241,11 +249,18 @@
A one-week jam to change the status quo.
-
+
{{ if gt .DaysUntilStart 0 }} Choose a project {{ else }} - + {{ if gt .DaysUntilEnd 0 }} + {{ if .SubmittedProjectUrl }} + Share your progress + {{ else }} + Choose a project + Create a jam project + {{ end }} + {{ end }} {{ end }} Join the Discord
@@ -266,6 +281,28 @@

+ {{ if .ShowcaseJson }} +
+
+ {{ if gt .DaysUntilEnd 0 }} +

Recent activity

+

+ These screenshots and videos were shared in #jam-showcase on our Discord. Join us! +

+ {{ else }} +

Showcase

+

+ Post-jam text +

+ {{ end }} +
+
+ See more +
+
+
+ {{ end }} +

Details / Rules

@@ -340,6 +377,109 @@ {{ template "footer.html" . }}
+ {{ template "showcase_templates.html" }} + + diff --git a/src/website/jam.go b/src/website/jam.go index 21b8610b..a87b4ae4 100644 --- a/src/website/jam.go +++ b/src/website/jam.go @@ -32,22 +32,132 @@ func JamIndex2022(c *RequestContext) ResponseData { type JamPageData struct { templates.BaseData DaysUntilStart, DaysUntilEnd int + SubmittedProjectUrl string + ProjectSubmissionUrl string + ShowcaseFeedUrl string + ShowcaseJson string } + var showcaseItems []templates.TimelineItem + submittedProjectUrl := "" + if daysUntilStart <= 0 && daysUntilEnd > 0 { + if c.CurrentUser != nil { + projects, err := hmndata.FetchProjects(c.Context(), c.Conn, c.CurrentUser, hmndata.ProjectsQuery{ + OwnerIDs: []int{c.CurrentUser.ID}, + JamSlugs: []string{hmndata.WRJ2022.Slug}, + Limit: 1, + }) + if err != nil { + return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch jam projects for current user")) + } + if len(projects) > 0 { + urlContext := hmndata.UrlContextForProject(&projects[0].Project) + submittedProjectUrl = urlContext.BuildHomepage() + } + } + + jamProjects, err := hmndata.FetchProjects(c.Context(), c.Conn, c.CurrentUser, hmndata.ProjectsQuery{ + JamSlugs: []string{hmndata.WRJ2022.Slug}, + }) + if err != nil { + return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch jam projects for current user")) + } + + jamProjectTags := make([]int, 0, len(jamProjects)) + for _, jp := range jamProjects { + if jp.Tag != nil { + jamProjectTags = append(jamProjectTags, jp.Tag.ID) + } + } + + snippets, err := hmndata.FetchSnippets(c.Context(), c.Conn, c.CurrentUser, hmndata.SnippetQuery{ + Tags: jamProjectTags, + Limit: 12, + }) + if err != nil { + return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch snippets for jam showcase")) + } + showcaseItems = make([]templates.TimelineItem, 0, len(snippets)) + for _, s := range snippets { + timelineItem := SnippetToTimelineItem(&s.Snippet, s.Asset, s.DiscordMessage, s.Tags, s.Owner, c.Theme) + if timelineItem.CanShowcase { + showcaseItems = append(showcaseItems, timelineItem) + } + } + } + showcaseJson := templates.TimelineItemsToJSON(showcaseItems) + res.MustWriteTemplate("wheeljam_2022_index.html", JamPageData{ - BaseData: baseData, - DaysUntilStart: daysUntilStart, - DaysUntilEnd: daysUntilEnd, + BaseData: baseData, + DaysUntilStart: daysUntilStart, + DaysUntilEnd: daysUntilEnd, + ProjectSubmissionUrl: hmnurl.BuildProjectNewJam(), + SubmittedProjectUrl: submittedProjectUrl, + ShowcaseFeedUrl: hmnurl.BuildJamFeed2022(), + ShowcaseJson: showcaseJson, }, c.Perf) return res } func JamFeed2022(c *RequestContext) ResponseData { - // List newly-created jam projects - // list snippets from jam projects - // list forum posts from jam project threads - // timeline everything - return FourOhFour(c) + jamProjects, err := hmndata.FetchProjects(c.Context(), c.Conn, c.CurrentUser, hmndata.ProjectsQuery{ + JamSlugs: []string{hmndata.WRJ2022.Slug}, + }) + if err != nil { + return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch jam projects for current user")) + } + + jamProjectTags := make([]int, 0, len(jamProjects)) + for _, jp := range jamProjects { + if jp.Tag != nil { + jamProjectTags = append(jamProjectTags, jp.Tag.ID) + } + } + + snippets, err := hmndata.FetchSnippets(c.Context(), c.Conn, c.CurrentUser, hmndata.SnippetQuery{ + Tags: jamProjectTags, + }) + if err != nil { + return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch snippets for jam showcase")) + } + + timelineItems := make([]templates.TimelineItem, 0, len(snippets)) + + for _, s := range snippets { + timelineItem := SnippetToTimelineItem(&s.Snippet, s.Asset, s.DiscordMessage, s.Tags, s.Owner, c.Theme) + timelineItems = append(timelineItems, timelineItem) + } + + // TODO(asaf): add forum posts from jam project threads to timeline + // TODO(asaf): Sort timeline items + + pageProjects := make([]templates.Project, 0, len(jamProjects)) + for _, p := range jamProjects { + pageProjects = append(pageProjects, templates.ProjectAndStuffToTemplate(&p, hmndata.UrlContextForProject(&p.Project).BuildHomepage(), c.Theme)) + } + + type JamFeedData struct { + templates.BaseData + JamProjects []templates.Project + TimelineItems []templates.TimelineItem + } + + baseData := getBaseDataAutocrumb(c, hmndata.WRJ2022.Name) + baseData.OpenGraphItems = []templates.OpenGraphItem{ + {Property: "og:site_name", Value: "Handmade.Network"}, + {Property: "og:type", Value: "website"}, + {Property: "og:image", Value: hmnurl.BuildPublic("wheeljam2022/opengraph.png", true)}, + {Property: "og:description", Value: "A one-week jam to change the status quo. August 15 - 21 on Handmade Network."}, + {Property: "og:url", Value: hmnurl.BuildJamIndex()}, + } + + var res ResponseData + res.MustWriteTemplate("wheeljam_2022_feed.html", JamFeedData{ + BaseData: baseData, + JamProjects: pageProjects, + TimelineItems: timelineItems, + }, c.Perf) + return res } func JamIndex2021(c *RequestContext) ResponseData {