Rework structure of project index. Need new copy.
This commit is contained in:
parent
45b4928d83
commit
25cc5ef11b
|
@ -7553,6 +7553,15 @@ article code {
|
|||
.g5 {
|
||||
gap: 4rem; }
|
||||
|
||||
.grid {
|
||||
display: grid; }
|
||||
|
||||
.grid-1 {
|
||||
grid-template-columns: 1fr; }
|
||||
|
||||
.grid-2 {
|
||||
grid-template-columns: 1fr 1fr; }
|
||||
|
||||
.aspect-ratio--2x1 {
|
||||
padding-bottom: 50%; }
|
||||
|
||||
|
@ -7582,6 +7591,10 @@ article code {
|
|||
column-gap: 2rem; }
|
||||
.cg5-ns {
|
||||
column-gap: 4rem; }
|
||||
.grid-1-ns {
|
||||
grid-template-columns: 1fr; }
|
||||
.grid-2-ns {
|
||||
grid-template-columns: 1fr 1fr; }
|
||||
.bg--dim-ns {
|
||||
background-color: #f0f0f0;
|
||||
background-color: var(--dim-background); } }
|
||||
|
@ -7607,6 +7620,10 @@ article code {
|
|||
column-gap: 2rem; }
|
||||
.cg5-m {
|
||||
column-gap: 4rem; }
|
||||
.grid-1-m {
|
||||
grid-template-columns: 1fr; }
|
||||
.grid-2-m {
|
||||
grid-template-columns: 1fr 1fr; }
|
||||
.bg--dim-m {
|
||||
background-color: #f0f0f0;
|
||||
background-color: var(--dim-background); } }
|
||||
|
@ -7632,6 +7649,10 @@ article code {
|
|||
column-gap: 2rem; }
|
||||
.cg5-l {
|
||||
column-gap: 4rem; }
|
||||
.grid-1-l {
|
||||
grid-template-columns: 1fr; }
|
||||
.grid-2-l {
|
||||
grid-template-columns: 1fr 1fr; }
|
||||
.bg--dim-l {
|
||||
background-color: #f0f0f0;
|
||||
background-color: var(--dim-background); } }
|
||||
|
|
|
@ -17,8 +17,9 @@ type Event struct {
|
|||
|
||||
type Jam struct {
|
||||
Event
|
||||
Name string
|
||||
Slug string
|
||||
Name string
|
||||
Slug string
|
||||
UrlSlug string
|
||||
}
|
||||
|
||||
var WRJ2021 = Jam{
|
||||
|
@ -26,8 +27,9 @@ var WRJ2021 = Jam{
|
|||
StartTime: time.Date(2021, 9, 27, 0, 0, 0, 0, time.UTC),
|
||||
EndTime: time.Date(2021, 10, 4, 0, 0, 0, 0, time.UTC),
|
||||
},
|
||||
Name: "Wheel Reinvention Jam 2021",
|
||||
Slug: "WRJ2021",
|
||||
Name: "Wheel Reinvention Jam 2021",
|
||||
Slug: "WRJ2021",
|
||||
UrlSlug: "2021",
|
||||
}
|
||||
|
||||
var WRJ2022 = Jam{
|
||||
|
@ -35,8 +37,9 @@ var WRJ2022 = Jam{
|
|||
StartTime: time.Date(2022, 8, 15, 0, 0, 0, 0, utils.Must1(time.LoadLocation("America/Los_Angeles"))),
|
||||
EndTime: time.Date(2022, 8, 22, 8, 0, 0, 0, utils.Must1(time.LoadLocation("America/Los_Angeles"))),
|
||||
},
|
||||
Name: "Wheel Reinvention Jam 2022",
|
||||
Slug: "WRJ2022",
|
||||
Name: "Wheel Reinvention Jam 2022",
|
||||
Slug: "WRJ2022",
|
||||
UrlSlug: "2022",
|
||||
}
|
||||
|
||||
var VJ2023 = Jam{
|
||||
|
@ -44,8 +47,9 @@ var VJ2023 = Jam{
|
|||
StartTime: time.Date(2023, 4, 14, 0, 0, 0, 0, time.UTC),
|
||||
EndTime: time.Date(2023, 4, 17, 0, 0, 0, 0, time.UTC),
|
||||
},
|
||||
Name: "Visibility Jam 2023",
|
||||
Slug: "VJ2023",
|
||||
Name: "Visibility Jam 2023",
|
||||
Slug: "VJ2023",
|
||||
UrlSlug: "visibility-2023",
|
||||
}
|
||||
|
||||
var WRJ2023 = Jam{
|
||||
|
@ -53,8 +57,9 @@ var WRJ2023 = Jam{
|
|||
StartTime: time.Date(2023, 9, 25, 10, 0, 0, 0, utils.Must1(time.LoadLocation("Europe/London"))),
|
||||
EndTime: time.Date(2023, 10, 1, 20, 0, 0, 0, utils.Must1(time.LoadLocation("Europe/London"))),
|
||||
},
|
||||
Name: "Wheel Reinvention Jam 2023",
|
||||
Slug: "WRJ2023",
|
||||
Name: "Wheel Reinvention Jam 2023",
|
||||
Slug: "WRJ2023",
|
||||
UrlSlug: "2023",
|
||||
}
|
||||
|
||||
var HMS2022 = Event{
|
||||
|
@ -84,6 +89,17 @@ func CurrentJam() *Jam {
|
|||
return nil
|
||||
}
|
||||
|
||||
func PreviousJam() *Jam {
|
||||
now := time.Now()
|
||||
var mostRecent *Jam
|
||||
for i, jam := range AllJams {
|
||||
if jam.EndTime.Before(now) {
|
||||
mostRecent = &AllJams[i]
|
||||
}
|
||||
}
|
||||
return mostRecent
|
||||
}
|
||||
|
||||
func JamBySlug(slug string) Jam {
|
||||
for _, jam := range AllJams {
|
||||
if jam.Slug == slug {
|
||||
|
|
|
@ -142,9 +142,11 @@ func TestFeed(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestProjectIndex(t *testing.T) {
|
||||
AssertRegexMatch(t, BuildProjectIndex(1), RegexProjectIndex, nil)
|
||||
AssertRegexMatch(t, BuildProjectIndex(2), RegexProjectIndex, map[string]string{"page": "2"})
|
||||
assert.Panics(t, func() { BuildProjectIndex(0) })
|
||||
AssertRegexMatch(t, BuildProjectIndex(1, ""), RegexProjectIndex, nil)
|
||||
AssertRegexMatch(t, BuildProjectIndex(2, ""), RegexProjectIndex, map[string]string{"page": "2"})
|
||||
AssertRegexMatch(t, BuildProjectIndex(1, "test"), RegexProjectIndex, map[string]string{"category": "test"})
|
||||
AssertRegexMatch(t, BuildProjectIndex(2, "test"), RegexProjectIndex, map[string]string{"page": "2", "category": "test"})
|
||||
assert.Panics(t, func() { BuildProjectIndex(0, "") })
|
||||
}
|
||||
|
||||
func TestProjectNew(t *testing.T) {
|
||||
|
@ -415,6 +417,16 @@ func TestJamRecap2023_Visibility(t *testing.T) {
|
|||
AssertSubdomain(t, BuildJamRecap2023_Visibility(), "")
|
||||
}
|
||||
|
||||
func TestJamIndex2023(t *testing.T) {
|
||||
AssertRegexMatch(t, BuildJamIndex2023(), RegexJamIndex2023, nil)
|
||||
AssertSubdomain(t, BuildJamIndex2023(), "")
|
||||
}
|
||||
|
||||
func TestJamFeed2023(t *testing.T) {
|
||||
AssertRegexMatch(t, BuildJamFeed2023(), RegexJamFeed2023, nil)
|
||||
AssertSubdomain(t, BuildJamFeed2023(), "")
|
||||
}
|
||||
|
||||
func TestTimeMachine(t *testing.T) {
|
||||
AssertRegexMatch(t, BuildTimeMachine(), RegexTimeMachine, nil)
|
||||
AssertSubdomain(t, BuildTimeMachine(), "")
|
||||
|
|
|
@ -126,6 +126,11 @@ func BuildJamRecap2023_Visibility() string {
|
|||
return Url("/jam/visibility-2023/recap", nil)
|
||||
}
|
||||
|
||||
func BuildJamIndexAny(slug string) string {
|
||||
defer CatchPanic()
|
||||
return Url(fmt.Sprintf("/jam/%s", slug), nil)
|
||||
}
|
||||
|
||||
var RegexTimeMachine = regexp.MustCompile("^/timemachine$")
|
||||
|
||||
func BuildTimeMachine() string {
|
||||
|
@ -445,17 +450,21 @@ func BuildAtomFeedForShowcase() string {
|
|||
* Projects
|
||||
*/
|
||||
|
||||
var RegexProjectIndex = regexp.MustCompile("^/projects(/(?P<page>.+)?)?$")
|
||||
var RegexProjectIndex = regexp.MustCompile(`^/projects(/(?P<category>[a-z][a-z0-9]+))?(/(?P<page>\d+))?$`)
|
||||
|
||||
func BuildProjectIndex(page int) string {
|
||||
func BuildProjectIndex(page int, category string) string {
|
||||
defer CatchPanic()
|
||||
if page < 1 {
|
||||
panic(oops.New(nil, "page must be >= 1"))
|
||||
}
|
||||
catpath := ""
|
||||
if category != "" {
|
||||
catpath = "/" + category
|
||||
}
|
||||
if page == 1 {
|
||||
return Url("/projects", nil)
|
||||
return Url(fmt.Sprintf("/projects%s", catpath), nil)
|
||||
} else {
|
||||
return Url(fmt.Sprintf("/projects/%d", page), nil)
|
||||
return Url(fmt.Sprintf("/projects%s/%d", catpath, page), nil)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -372,6 +372,18 @@ article code {
|
|||
.g4 { gap: $spacing-large; }
|
||||
.g5 { gap: $spacing-extra-large; }
|
||||
|
||||
.grid {
|
||||
display: grid;
|
||||
}
|
||||
|
||||
.grid-1 {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.grid-2 {
|
||||
grid-template-columns: 1fr 1fr;
|
||||
}
|
||||
|
||||
.aspect-ratio--2x1 {
|
||||
padding-bottom: 50%;
|
||||
}
|
||||
|
@ -392,6 +404,8 @@ article code {
|
|||
.cg3-ns { column-gap: $spacing-medium; }
|
||||
.cg4-ns { column-gap: $spacing-large; }
|
||||
.cg5-ns { column-gap: $spacing-extra-large; }
|
||||
.grid-1-ns { grid-template-columns: 1fr; }
|
||||
.grid-2-ns { grid-template-columns: 1fr 1fr; }
|
||||
|
||||
.bg--dim-ns {
|
||||
@include usevar(background-color, dim-background);
|
||||
|
@ -409,6 +423,8 @@ article code {
|
|||
.cg3-m { column-gap: $spacing-medium; }
|
||||
.cg4-m { column-gap: $spacing-large; }
|
||||
.cg5-m { column-gap: $spacing-extra-large; }
|
||||
.grid-1-m { grid-template-columns: 1fr; }
|
||||
.grid-2-m { grid-template-columns: 1fr 1fr; }
|
||||
|
||||
.bg--dim-m {
|
||||
@include usevar(background-color, dim-background);
|
||||
|
@ -426,6 +442,8 @@ article code {
|
|||
.cg3-l { column-gap: $spacing-medium; }
|
||||
.cg4-l { column-gap: $spacing-large; }
|
||||
.cg5-l { column-gap: $spacing-extra-large; }
|
||||
.grid-1-l { grid-template-columns: 1fr; }
|
||||
.grid-2-l { grid-template-columns: 1fr 1fr; }
|
||||
|
||||
.bg--dim-l {
|
||||
@include usevar(background-color, dim-background);
|
||||
|
|
|
@ -4,13 +4,13 @@
|
|||
<script src="{{ static "js/carousel.js" }}"></script>
|
||||
{{ end }}
|
||||
|
||||
{{ define "content" }}
|
||||
{{ define "all_projects" }}
|
||||
<div>
|
||||
{{ with .CarouselProjects }}
|
||||
{{ with .OfficialProjects }}
|
||||
<div class="carousel-container project-carousel mw-100 mv2 mv3-ns margin-center dn db-ns">
|
||||
<div class="carousel pa3 h5 overflow-hidden bg--dim br2-ns">
|
||||
{{ range $index, $project := . }}
|
||||
<div class="carousel-item flex pa3 w-100 h-100 bg--dim items-center {{ if eq $index 0 }}active{{ end }}">
|
||||
<div class="carousel-item flex pv3 pl3 w-100 h-100 bg--dim items-center {{ if eq $index 0 }}active{{ end }}">
|
||||
<div class="flex-grow-1 pr3 relative flex flex-column h-100 justify-center">
|
||||
<a href="{{ $project.Url }}">
|
||||
<h3>{{ $project.Name }}</h3>
|
||||
|
@ -31,42 +31,55 @@
|
|||
<div class="carousel-buttons pv2"></div>
|
||||
</div>
|
||||
{{ end }}
|
||||
<div class="flex flex-column flex-row-l mv3 items-start">
|
||||
<div class="bg--dim-ns br2">
|
||||
<div class="clear"></div>
|
||||
<div class="optionbar pv2 ph3">
|
||||
<div class="options">
|
||||
<a href="{{ .ProjectAtomFeedUrl }}"><span class="icon big">4</span> RSS Feed – New Projects</span></a>
|
||||
</div>
|
||||
<div class="options">
|
||||
{{ template "pagination.html" .Pagination }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="projectlist ph3">
|
||||
{{ range .Projects }}
|
||||
<div class="mv3">
|
||||
<div class="flex flex-column g3">
|
||||
{{ if .OfficialProjects }}
|
||||
<div class="ph3 pt3 bg--dim br2 flex flex-column">
|
||||
<h2>Official Projects</h2>
|
||||
<p>These projects are boop boop hahahaa:</p>
|
||||
<div class="grid grid-1 grid-2-ns g3">
|
||||
{{ range .OfficialProjects }}
|
||||
{{ template "project_card.html" projectcarddata . "" }}
|
||||
</div>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
</div>
|
||||
<a href="{{ .OfficialProjectsLink }}" class="pa3 tc">See more »</a>
|
||||
</div>
|
||||
|
||||
<div class="optionbar bottom pv2 ph3">
|
||||
<div class="options order-1"></div>
|
||||
<div class="options order-0 order-last-ns">{{ template "pagination.html" .Pagination }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-100 w-40-l ph3 ph0-ns flex-shrink-0">
|
||||
<div class="ml3-l mt3 mt0-l pa3 bg--dim br2">
|
||||
{{ end }}
|
||||
{{ if .PersonalProjects }}
|
||||
<div class="ph3 pt3 bg--dim br2 flex flex-column">
|
||||
<h2>Personal Projects</h2>
|
||||
<p>Many community members have projects of their own that are currently works in progress. Here's a few:</p>
|
||||
{{ range .PersonalProjects }}
|
||||
<div class="mv3">
|
||||
<p>Many community members have projects of their own. Here's a few:</p>
|
||||
<div class="grid grid-1 grid-2-ns g3">
|
||||
{{ range .PersonalProjects }}
|
||||
{{ template "project_card.html" projectcarddata . "" }}
|
||||
</div>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
</div>
|
||||
<a href="{{ .PersonalProjectsLink }}" class="pa3 tc">See more »</a>
|
||||
</div>
|
||||
</div>
|
||||
{{ end }}
|
||||
{{ if .CurrentJamProjects }}
|
||||
<div class="ph3 pt3 bg--dim br2 flex flex-column">
|
||||
<h2>JAM! NOWS!</h2>
|
||||
<p>wowowowow</p>
|
||||
<div class="grid grid-1 grid-2-ns g3">
|
||||
{{ range .CurrentJamProjects }}
|
||||
{{ template "project_card.html" projectcarddata . "" }}
|
||||
{{ end }}
|
||||
</div>
|
||||
<a href="{{ .CurrentJamLink }}" class="pa3 tc">See more »</a>
|
||||
</div>
|
||||
{{ end }}
|
||||
{{ if .PreviousJamProjects }}
|
||||
<div class="ph3 pt3 bg--dim br2 flex flex-column">
|
||||
<h2>JAM! THENS!</h2>
|
||||
<p>nonononoono</p>
|
||||
<div class="grid grid-1 grid-2-ns g3">
|
||||
{{ range .PreviousJamProjects }}
|
||||
{{ template "project_card.html" projectcarddata . "" }}
|
||||
{{ end }}
|
||||
</div>
|
||||
<a href="{{ .PreviousJamLink }}" class="pa3 tc">See more »</a>
|
||||
</div>
|
||||
{{ end }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -76,3 +89,34 @@
|
|||
});
|
||||
</script>
|
||||
{{ end }}
|
||||
|
||||
{{ define "single_category" }}
|
||||
<div class="bg--dim-ns br2">
|
||||
<div class="clear"></div>
|
||||
<div class="optionbar pv2 ph3">
|
||||
<div class="options"></div>
|
||||
<div class="options">
|
||||
{{ template "pagination.html" .Pagination }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="projectlist pa3 grid grid-1 grid-2-ns g3">
|
||||
{{ range .PageProjects }}
|
||||
{{ template "project_card.html" projectcarddata . "" }}
|
||||
{{ end }}
|
||||
</div>
|
||||
|
||||
<div class="optionbar bottom pv2 ph3">
|
||||
<div class="options order-1"></div>
|
||||
<div class="options order-0 order-last-ns">{{ template "pagination.html" .Pagination }}</div>
|
||||
</div>
|
||||
</div>
|
||||
{{ end }}
|
||||
|
||||
{{ define "content" }}
|
||||
{{ if .AllProjects }}
|
||||
{{ template "all_projects" . }}
|
||||
{{ else }}
|
||||
{{ template "single_category" . }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
|
|
@ -72,7 +72,7 @@ func getBaseData(c *RequestContext, title string, breadcrumbs []templates.Breadc
|
|||
LoginWithDiscordUrl: hmnurl.BuildLoginWithDiscord(c.FullUrl()),
|
||||
|
||||
HMNHomepageUrl: hmnurl.BuildHomepage(),
|
||||
ProjectIndexUrl: hmnurl.BuildProjectIndex(1),
|
||||
ProjectIndexUrl: hmnurl.BuildProjectIndex(1, ""),
|
||||
PodcastUrl: hmnurl.BuildPodcast(),
|
||||
FishbowlUrl: hmnurl.BuildFishbowlIndex(),
|
||||
ForumsUrl: hmnurl.HMNProjectContext.BuildForum(nil, 1),
|
||||
|
@ -85,7 +85,7 @@ func getBaseData(c *RequestContext, title string, breadcrumbs []templates.Breadc
|
|||
AboutUrl: hmnurl.BuildAbout(),
|
||||
ManifestoUrl: hmnurl.BuildManifesto(),
|
||||
CommunicationGuidelinesUrl: hmnurl.BuildCommunicationGuidelines(),
|
||||
ProjectIndexUrl: hmnurl.BuildProjectIndex(1),
|
||||
ProjectIndexUrl: hmnurl.BuildProjectIndex(1, ""),
|
||||
RolesUrl: hmnurl.BuildStaffRolesIndex(),
|
||||
ContactUrl: hmnurl.BuildContactPage(),
|
||||
SearchActionUrl: "https://duckduckgo.com",
|
||||
|
|
|
@ -149,7 +149,7 @@ func AtomFeed(c *RequestContext) ResponseData {
|
|||
feedData.FeedType = FeedTypeProjects
|
||||
feedData.FeedID = FeedIDProjects
|
||||
feedData.AtomFeedUrl = hmnurl.BuildAtomFeedForProjects()
|
||||
feedData.FeedUrl = hmnurl.BuildProjectIndex(1)
|
||||
feedData.FeedUrl = hmnurl.BuildProjectIndex(1, "")
|
||||
|
||||
c.Perf.StartBlock("SQL", "Fetching projects")
|
||||
_, hasAll := c.Req.URL.Query()["all"]
|
||||
|
|
|
@ -6,7 +6,6 @@ import (
|
|||
"fmt"
|
||||
"image"
|
||||
"io"
|
||||
"math"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
"path"
|
||||
|
@ -29,7 +28,7 @@ import (
|
|||
"github.com/teacat/noire"
|
||||
)
|
||||
|
||||
const maxPersonalProjects = 5
|
||||
const maxPersonalProjects = 10
|
||||
const maxProjectOwners = 5
|
||||
|
||||
func ProjectCSS(c *RequestContext) ResponseData {
|
||||
|
@ -75,41 +74,143 @@ func ProjectCSS(c *RequestContext) ResponseData {
|
|||
type ProjectTemplateData struct {
|
||||
templates.BaseData
|
||||
|
||||
Pagination templates.Pagination
|
||||
CarouselProjects []templates.Project
|
||||
Projects []templates.Project
|
||||
PersonalProjects []templates.Project
|
||||
AllProjects bool
|
||||
|
||||
ProjectAtomFeedUrl string
|
||||
WIPForumUrl string
|
||||
// Stuff for all projects
|
||||
OfficialProjects []templates.Project
|
||||
OfficialProjectsLink string
|
||||
PersonalProjects []templates.Project
|
||||
PersonalProjectsLink string
|
||||
CurrentJamProjects []templates.Project
|
||||
CurrentJamLink string
|
||||
PreviousJamProjects []templates.Project
|
||||
PreviousJamLink string
|
||||
|
||||
// Stuff for pages of projects only
|
||||
Pagination templates.Pagination
|
||||
PageProjects []templates.Project
|
||||
}
|
||||
|
||||
func ProjectIndex(c *RequestContext) ResponseData {
|
||||
const projectsPerPage = 20
|
||||
const maxCarouselProjects = 10
|
||||
const maxPersonalProjects = 10
|
||||
cat := c.PathParams["category"]
|
||||
pageStr := c.PathParams["page"]
|
||||
|
||||
officialProjects, err := hmndata.FetchProjects(c, c.Conn, c.CurrentUser, hmndata.ProjectsQuery{
|
||||
currentJam := hmndata.CurrentJam()
|
||||
previousJam := hmndata.PreviousJam()
|
||||
|
||||
if cat == "" && pageStr == "" {
|
||||
const projectsPerSection = 8
|
||||
|
||||
officialProjects, err := getShuffledOfficialProjects(c)
|
||||
if err != nil {
|
||||
return c.ErrorResponse(http.StatusInternalServerError, err)
|
||||
}
|
||||
|
||||
personalProjects, err := getPersonalProjects(c, "")
|
||||
if err != nil {
|
||||
return c.ErrorResponse(http.StatusInternalServerError, err)
|
||||
}
|
||||
|
||||
var currentJamProjects []templates.Project
|
||||
if currentJam != nil {
|
||||
var err error
|
||||
currentJamProjects, err = getPersonalProjects(c, currentJam.Slug)
|
||||
if err != nil {
|
||||
return c.ErrorResponse(http.StatusInternalServerError, err)
|
||||
}
|
||||
}
|
||||
|
||||
previousJamProjects, err := getPersonalProjects(c, previousJam.Slug)
|
||||
if err != nil {
|
||||
return c.ErrorResponse(http.StatusInternalServerError, err)
|
||||
}
|
||||
|
||||
baseData := getBaseDataAutocrumb(c, "Projects")
|
||||
tmpl := ProjectTemplateData{
|
||||
BaseData: baseData,
|
||||
|
||||
AllProjects: true,
|
||||
|
||||
OfficialProjects: officialProjects[:utils.IntMin(len(officialProjects), projectsPerSection)],
|
||||
OfficialProjectsLink: hmnurl.BuildProjectIndex(1, "official"),
|
||||
PersonalProjects: personalProjects[:utils.IntMin(len(personalProjects), projectsPerSection)],
|
||||
PersonalProjectsLink: hmnurl.BuildProjectIndex(1, "personal"),
|
||||
CurrentJamProjects: currentJamProjects[:utils.IntMin(len(currentJamProjects), projectsPerSection)],
|
||||
// CurrentJamLink set later
|
||||
PreviousJamProjects: previousJamProjects[:utils.IntMin(len(previousJamProjects), projectsPerSection)],
|
||||
PreviousJamLink: hmnurl.BuildJamIndexAny(previousJam.UrlSlug),
|
||||
}
|
||||
|
||||
if hmndata.CurrentJam() != nil {
|
||||
tmpl.CurrentJamLink = hmnurl.BuildJamIndexAny(hmndata.CurrentJam().UrlSlug)
|
||||
}
|
||||
|
||||
var res ResponseData
|
||||
res.MustWriteTemplate("project_index.html", tmpl, c.Perf)
|
||||
return res
|
||||
} else {
|
||||
const projectsPerPage = 20
|
||||
|
||||
var projects []templates.Project
|
||||
var err error
|
||||
switch cat {
|
||||
case hmndata.WRJ2022.UrlSlug:
|
||||
projects, err = getPersonalProjects(c, hmndata.WRJ2022.Slug)
|
||||
case hmndata.VJ2023.Slug:
|
||||
projects, err = getPersonalProjects(c, hmndata.VJ2023.Slug)
|
||||
case hmndata.WRJ2023.Slug:
|
||||
projects, err = getPersonalProjects(c, hmndata.WRJ2023.Slug)
|
||||
case "personal":
|
||||
projects, err = getPersonalProjects(c, "")
|
||||
case "official":
|
||||
projects, err = getShuffledOfficialProjects(c)
|
||||
default:
|
||||
return c.Redirect(hmnurl.BuildProjectIndex(1, ""), http.StatusSeeOther)
|
||||
}
|
||||
if err != nil {
|
||||
return c.ErrorResponse(http.StatusInternalServerError, err)
|
||||
}
|
||||
|
||||
page, numPages, ok := getPageInfo(pageStr, len(projects), projectsPerPage)
|
||||
if !ok {
|
||||
return c.Redirect(hmnurl.BuildProjectIndex(1, cat), http.StatusSeeOther)
|
||||
}
|
||||
pagination := templates.Pagination{
|
||||
Current: page,
|
||||
Total: numPages,
|
||||
|
||||
FirstUrl: hmnurl.BuildProjectIndex(1, cat),
|
||||
LastUrl: hmnurl.BuildProjectIndex(numPages, cat),
|
||||
NextUrl: hmnurl.BuildProjectIndex(utils.IntClamp(1, page+1, numPages), cat),
|
||||
PreviousUrl: hmnurl.BuildProjectIndex(utils.IntClamp(1, page-1, numPages), cat),
|
||||
}
|
||||
|
||||
firstProjectIndex := (page - 1) * projectsPerPage
|
||||
endIndex := utils.IntMin(firstProjectIndex+projectsPerPage, len(projects))
|
||||
pageProjects := projects[firstProjectIndex:endIndex]
|
||||
|
||||
baseData := getBaseData(c, "Projects", []templates.Breadcrumb{
|
||||
{"Projects", hmnurl.BuildProjectIndex(1, "")},
|
||||
})
|
||||
var res ResponseData
|
||||
res.MustWriteTemplate("project_index.html", ProjectTemplateData{
|
||||
BaseData: baseData,
|
||||
|
||||
AllProjects: false,
|
||||
|
||||
Pagination: pagination,
|
||||
PageProjects: pageProjects,
|
||||
}, c.Perf)
|
||||
return res
|
||||
}
|
||||
}
|
||||
|
||||
func getShuffledOfficialProjects(c *RequestContext) ([]templates.Project, error) {
|
||||
official, err := hmndata.FetchProjects(c, c.Conn, c.CurrentUser, hmndata.ProjectsQuery{
|
||||
Types: hmndata.OfficialProjects,
|
||||
})
|
||||
if err != nil {
|
||||
return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch projects"))
|
||||
}
|
||||
|
||||
numPages := int(math.Ceil(float64(len(officialProjects)) / projectsPerPage))
|
||||
page, numPages, ok := getPageInfo(c.PathParams["page"], len(officialProjects), feedPostsPerPage)
|
||||
if !ok {
|
||||
return c.Redirect(hmnurl.BuildProjectIndex(1), http.StatusSeeOther)
|
||||
}
|
||||
|
||||
pagination := templates.Pagination{
|
||||
Current: page,
|
||||
Total: numPages,
|
||||
|
||||
FirstUrl: hmnurl.BuildProjectIndex(1),
|
||||
LastUrl: hmnurl.BuildProjectIndex(numPages),
|
||||
NextUrl: hmnurl.BuildProjectIndex(utils.IntClamp(1, page+1, numPages)),
|
||||
PreviousUrl: hmnurl.BuildProjectIndex(utils.IntClamp(1, page-1, numPages)),
|
||||
return nil, oops.New(err, "failed to fetch projects")
|
||||
}
|
||||
|
||||
c.Perf.StartBlock("PROJECTS", "Grouping and sorting")
|
||||
|
@ -118,7 +219,7 @@ func ProjectIndex(c *RequestContext) ResponseData {
|
|||
var recentProjects []templates.Project
|
||||
var restProjects []templates.Project
|
||||
now := time.Now()
|
||||
for _, p := range officialProjects {
|
||||
for _, p := range official {
|
||||
templateProject := templates.ProjectAndStuffToTemplate(&p, hmndata.UrlContextForProject(&p.Project).BuildHomepage(), c.Theme)
|
||||
|
||||
if p.Project.Slug == "hero" {
|
||||
|
@ -146,60 +247,58 @@ func ProjectIndex(c *RequestContext) ResponseData {
|
|||
featuredProjects = append([]templates.Project{*handmadeHero}, featuredProjects...)
|
||||
}
|
||||
|
||||
orderedProjects := make([]templates.Project, 0, len(featuredProjects)+len(recentProjects)+len(restProjects))
|
||||
orderedProjects = append(orderedProjects, featuredProjects...)
|
||||
orderedProjects = append(orderedProjects, recentProjects...)
|
||||
orderedProjects = append(orderedProjects, restProjects...)
|
||||
officialProjects := make([]templates.Project, 0, len(featuredProjects)+len(recentProjects)+len(restProjects))
|
||||
officialProjects = append(officialProjects, featuredProjects...)
|
||||
officialProjects = append(officialProjects, recentProjects...)
|
||||
officialProjects = append(officialProjects, restProjects...)
|
||||
|
||||
firstProjectIndex := (page - 1) * projectsPerPage
|
||||
endIndex := utils.IntMin(firstProjectIndex+projectsPerPage, len(orderedProjects))
|
||||
pageProjects := orderedProjects[firstProjectIndex:endIndex]
|
||||
|
||||
var carouselProjects []templates.Project
|
||||
if page == 1 {
|
||||
carouselProjects = featuredProjects[:utils.IntMin(len(featuredProjects), maxCarouselProjects)]
|
||||
}
|
||||
c.Perf.EndBlock()
|
||||
|
||||
// Fetch and highlight a random selection of personal projects
|
||||
var personalProjects []templates.Project
|
||||
{
|
||||
projects, err := hmndata.FetchProjects(c, c.Conn, c.CurrentUser, hmndata.ProjectsQuery{
|
||||
Types: hmndata.PersonalProjects,
|
||||
})
|
||||
if err != nil {
|
||||
return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch personal projects"))
|
||||
}
|
||||
return officialProjects, nil
|
||||
}
|
||||
|
||||
sort.Slice(projects, func(i, j int) bool {
|
||||
p1 := projects[i].Project
|
||||
p2 := projects[j].Project
|
||||
return p2.AllLastUpdated.Before(p1.AllLastUpdated) // sort backwards - recent first
|
||||
})
|
||||
|
||||
for i, p := range projects {
|
||||
if i >= maxPersonalProjects {
|
||||
break
|
||||
}
|
||||
templateProject := templates.ProjectAndStuffToTemplate(&p, hmndata.UrlContextForProject(&p.Project).BuildHomepage(), c.Theme)
|
||||
personalProjects = append(personalProjects, templateProject)
|
||||
}
|
||||
func getPersonalProjects(c *RequestContext, jamSlug string) ([]templates.Project, error) {
|
||||
var slugs []string
|
||||
if jamSlug != "" {
|
||||
slugs = []string{jamSlug}
|
||||
}
|
||||
|
||||
baseData := getBaseDataAutocrumb(c, "Projects")
|
||||
var res ResponseData
|
||||
res.MustWriteTemplate("project_index.html", ProjectTemplateData{
|
||||
BaseData: baseData,
|
||||
projects, err := hmndata.FetchProjects(c, c.Conn, c.CurrentUser, hmndata.ProjectsQuery{
|
||||
Types: hmndata.PersonalProjects,
|
||||
JamSlugs: slugs,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, oops.New(err, "failed to fetch personal projects")
|
||||
}
|
||||
|
||||
Pagination: pagination,
|
||||
CarouselProjects: carouselProjects,
|
||||
Projects: pageProjects,
|
||||
PersonalProjects: personalProjects,
|
||||
sort.Slice(projects, func(i, j int) bool {
|
||||
p1 := projects[i].Project
|
||||
p2 := projects[j].Project
|
||||
return p2.AllLastUpdated.Before(p1.AllLastUpdated) // sort backwards - recent first
|
||||
})
|
||||
|
||||
ProjectAtomFeedUrl: hmnurl.BuildAtomFeedForProjects(),
|
||||
WIPForumUrl: hmnurl.HMNProjectContext.BuildForum([]string{"wip"}, 1),
|
||||
}, c.Perf)
|
||||
return res
|
||||
var personalProjects []templates.Project
|
||||
for _, p := range projects {
|
||||
templateProject := templates.ProjectAndStuffToTemplate(&p, hmndata.UrlContextForProject(&p.Project).BuildHomepage(), c.Theme)
|
||||
personalProjects = append(personalProjects, templateProject)
|
||||
}
|
||||
|
||||
return personalProjects, nil
|
||||
}
|
||||
|
||||
func jamLink(jamSlug string) string {
|
||||
switch jamSlug {
|
||||
case hmndata.WRJ2021.Slug:
|
||||
return hmnurl.BuildJamIndex2021()
|
||||
case hmndata.WRJ2022.Slug:
|
||||
return hmnurl.BuildJamIndex2022()
|
||||
case hmndata.WRJ2023.Slug:
|
||||
return hmnurl.BuildJamIndex2023()
|
||||
case hmndata.VJ2023.Slug:
|
||||
return hmnurl.BuildJamIndex2023_Visibility()
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
type ProjectHomepageData struct {
|
||||
|
|
Loading…
Reference in New Issue