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