Add back project nav
This commit is contained in:
parent
5eff3c38b4
commit
4e47c51fa1
109
public/style.css
109
public/style.css
|
@ -8726,8 +8726,22 @@ header .hmn-logo {
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
color: white !important; }
|
color: white !important; }
|
||||||
@media screen and (min-width: 30em) {
|
@media screen and (min-width: 30em) {
|
||||||
header .hmn-logo {
|
header .hmn-logo.big {
|
||||||
width: 11.25rem; } }
|
width: 11.25rem; } }
|
||||||
|
@media screen and (min-width: 30em) {
|
||||||
|
header .hmn-logo.small {
|
||||||
|
width: 3.75rem;
|
||||||
|
padding: 0.8rem;
|
||||||
|
text-align: justify;
|
||||||
|
text-justify: inter-character;
|
||||||
|
flex-direction: column;
|
||||||
|
font-size: 1rem;
|
||||||
|
line-height: 1em;
|
||||||
|
align-items: stretch; }
|
||||||
|
header .hmn-logo.small div::after {
|
||||||
|
content: '';
|
||||||
|
display: inline-block;
|
||||||
|
width: 100%; } }
|
||||||
|
|
||||||
header .items {
|
header .items {
|
||||||
position: relative; }
|
position: relative; }
|
||||||
|
@ -9324,51 +9338,54 @@ span.icon-rss::before {
|
||||||
width: auto;
|
width: auto;
|
||||||
max-height: calc(100vh - 2rem); } }
|
max-height: calc(100vh - 2rem); } }
|
||||||
|
|
||||||
.carousel-container {
|
.carousel-container .carousel {
|
||||||
width: 50rem; }
|
box-sizing: content-box;
|
||||||
.carousel-container .carousel {
|
position: relative; }
|
||||||
box-sizing: content-box;
|
|
||||||
position: relative; }
|
.carousel-container .carousel-item {
|
||||||
.carousel-container .carousel-item {
|
position: absolute;
|
||||||
position: absolute;
|
top: 0;
|
||||||
top: 0;
|
left: 0; }
|
||||||
left: 0; }
|
.carousel-container .carousel-item:not(.active) {
|
||||||
.carousel-container .carousel-item:not(.active) {
|
display: none; }
|
||||||
display: none; }
|
.carousel-container .carousel-item br {
|
||||||
.carousel-container .carousel-item br {
|
line-height: 0.6em; }
|
||||||
line-height: 0.6em; }
|
|
||||||
.carousel-container .carousel-description {
|
.carousel-container .carousel-description {
|
||||||
max-height: 14rem;
|
max-height: 14rem;
|
||||||
overflow: hidden; }
|
overflow: hidden; }
|
||||||
.carousel-container .carousel-fade {
|
|
||||||
position: absolute;
|
.carousel-container .carousel-fade {
|
||||||
left: 0;
|
position: absolute;
|
||||||
right: 0;
|
left: 0;
|
||||||
bottom: 0;
|
right: 0;
|
||||||
height: 30px;
|
bottom: 0;
|
||||||
background: linear-gradient( rgba(240, 240, 240, 0) , #f0f0f0 );
|
height: 30px;
|
||||||
background: linear-gradient( var(--dim-background-transparent) , var(--dim-background) ); }
|
background: linear-gradient( rgba(240, 240, 240, 0) , #f0f0f0 );
|
||||||
.carousel-container .carousel-item-small {
|
background: linear-gradient( var(--dim-background-transparent) , var(--dim-background) ); }
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
.carousel-container .carousel-item-small {
|
||||||
left: 0; }
|
position: absolute;
|
||||||
.carousel-container .carousel-item-small:not(.active) {
|
top: 0;
|
||||||
display: none; }
|
left: 0; }
|
||||||
.carousel-container .carousel-button {
|
.carousel-container .carousel-item-small:not(.active) {
|
||||||
border: 1px solid;
|
display: none; }
|
||||||
border-color: #999;
|
|
||||||
border-color: var(--dimmer-color);
|
.carousel-container .carousel-button {
|
||||||
cursor: pointer;
|
border: 1px solid;
|
||||||
transition: all 100ms ease-in-out; }
|
border-color: #999;
|
||||||
.carousel-container .carousel-button:hover {
|
border-color: var(--dimmer-color);
|
||||||
background-color: #bbb;
|
cursor: pointer;
|
||||||
background-color: var(--dimmest-color); }
|
transition: all 100ms ease-in-out; }
|
||||||
.carousel-container .carousel-button.active {
|
.carousel-container .carousel-button:hover {
|
||||||
border-color: #666;
|
background-color: #bbb;
|
||||||
border-color: var(--theme-color); }
|
background-color: var(--dimmest-color); }
|
||||||
.carousel-container .carousel-button.active:hover {
|
.carousel-container .carousel-button.active {
|
||||||
background-color: #ccc;
|
border-color: #666;
|
||||||
background-color: var(--theme-color-dimmest); }
|
border-color: var(--theme-color); }
|
||||||
|
.carousel-container .carousel-button.active:hover {
|
||||||
|
background-color: #ccc;
|
||||||
|
background-color: var(--theme-color-dimmest); }
|
||||||
|
|
||||||
.notice {
|
.notice {
|
||||||
color: #fff;
|
color: #fff;
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
.carousel-container {
|
.carousel-container {
|
||||||
width: 50rem;
|
|
||||||
|
|
||||||
.carousel {
|
.carousel {
|
||||||
box-sizing: content-box;
|
box-sizing: content-box;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
|
@ -13,8 +13,29 @@ header {
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
color: white !important;
|
color: white !important;
|
||||||
|
|
||||||
@media #{$breakpoint-not-small} {
|
&.big {
|
||||||
width: px2rem(180px);
|
@media #{$breakpoint-not-small} {
|
||||||
|
width: px2rem(180px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.small {
|
||||||
|
@media #{$breakpoint-not-small} {
|
||||||
|
width: $logo-height;
|
||||||
|
padding: 0.8rem;
|
||||||
|
text-align: justify;
|
||||||
|
text-justify: inter-character;
|
||||||
|
flex-direction: column;
|
||||||
|
font-size: 1rem;
|
||||||
|
line-height: 1em;
|
||||||
|
align-items: stretch;
|
||||||
|
|
||||||
|
div::after {
|
||||||
|
content: '';
|
||||||
|
display: inline-block;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,35 +26,65 @@
|
||||||
{{ end }}
|
{{ end }}
|
||||||
</div>
|
</div>
|
||||||
<div class="menu-bar flex flex-column flex-row-ns justify-between {{ if .IsProjectPage }}project{{ end }}">
|
<div class="menu-bar flex flex-column flex-row-ns justify-between {{ if .IsProjectPage }}project{{ end }}">
|
||||||
<div class="flex flex-column flex-row-ns">
|
<div class="flex flex-column flex-row-ns items-center">
|
||||||
<a href="{{ .Header.HMNHomepageUrl }}" class="hmn-logo bg-theme-dark">
|
{{ $itemsClass := "items flex items-center justify-center justify-start-ns ml2-ns ml3-l" }}
|
||||||
Handmade
|
{{ if .Header.Project }}
|
||||||
</a>
|
<a href="{{ .Header.HMNHomepageUrl }}" class="hmn-logo small bg-theme-dark">
|
||||||
<div class="items flex items-center justify-center justify-start-ns ml2-ns ml3-l">
|
<div>Hand</div>
|
||||||
<div class="root-item">
|
<div>made</div>
|
||||||
<a href="{{ .Header.ProjectIndexUrl }}">Projects</a>
|
</a>
|
||||||
</div>
|
<a href="{{ .Project.Url }}">
|
||||||
<div class="root-item">
|
<h2 class="mb0 mt2 mt0-ns ml3-ns">{{ .Project.Name }}</h2>
|
||||||
<a>Media <div class="dib svgicon ml1">{{ svg "chevron-down-thick" }}</div></a>
|
</a>
|
||||||
<div class="submenu b--theme-dark">
|
{{ with .Header.Project }}
|
||||||
<a href="{{ .Header.PodcastUrl }}">Podcast</a>
|
<div class="{{ $itemsClass }}">
|
||||||
<a href="https://handmadedev.show/" target="_blank">Handmade Dev Show</a>
|
{{ if .HasBlog }}
|
||||||
|
<div class="root-item">
|
||||||
|
<a href="{{ .BlogUrl }}">Blog</a>
|
||||||
|
</div>
|
||||||
|
{{ end }}
|
||||||
|
{{ if .HasForums }}
|
||||||
|
<div class="root-item">
|
||||||
|
<a href="{{ .ForumsUrl }}">Forums</a>
|
||||||
|
</div>
|
||||||
|
{{ end }}
|
||||||
|
{{ if .HasEpisodeGuide }}
|
||||||
|
<div class="root-item">
|
||||||
|
<a href="{{ .EpisodeGuideUrl }}">Episode Guide</a>
|
||||||
|
</div>
|
||||||
|
{{ end }}
|
||||||
|
</div>
|
||||||
|
{{ end }}
|
||||||
|
{{ else }}
|
||||||
|
<a href="{{ .Header.HMNHomepageUrl }}" class="hmn-logo big bg-theme-dark">
|
||||||
|
Handmade
|
||||||
|
</a>
|
||||||
|
<div class="{{ $itemsClass }}">
|
||||||
|
<div class="root-item">
|
||||||
|
<a href="{{ .Header.ProjectIndexUrl }}">Projects</a>
|
||||||
|
</div>
|
||||||
|
<div class="root-item">
|
||||||
|
<a>Media <div class="dib svgicon ml1">{{ svg "chevron-down-thick" }}</div></a>
|
||||||
|
<div class="submenu b--theme-dark">
|
||||||
|
<a href="{{ .Header.PodcastUrl }}">Podcast</a>
|
||||||
|
<a href="https://handmadedev.show/" target="_blank">Handmade Dev Show</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="root-item">
|
||||||
|
<a href="{{ .Header.ForumsUrl }}">Forums</a>
|
||||||
|
</div>
|
||||||
|
<div class="root-item">
|
||||||
|
<a>Resources <div class="dib svgicon ml1">{{ svg "chevron-down-thick" }}</div></a>
|
||||||
|
<div class="submenu b--theme-dark">
|
||||||
|
<a href="{{ .Header.LibraryUrl }}">Library</a>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="root-item">
|
{{ end }}
|
||||||
<a href="{{ .Header.ForumsUrl }}">Forums</a>
|
|
||||||
</div>
|
|
||||||
<div class="root-item">
|
|
||||||
<a>Resources <div class="dib svgicon ml1">{{ svg "chevron-down-thick" }}</div></a>
|
|
||||||
<div class="submenu b--theme-dark">
|
|
||||||
<a href="{{ .Header.LibraryUrl }}">Library</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="dn flex-ns items-center f3">
|
<div class="dn flex-ns items-center f3">
|
||||||
<a class="svgicon" href="https://twitter.com/handmade_net/" target="_blank">{{ svg "twitter" }}</a>
|
<a class="svgicon" href="https://twitter.com/handmade_net/" target="_blank">{{ svg "twitter" }}</a>
|
||||||
<a class="svgicon ml2" href="{{ .DiscordUrl }}" target="_blank">{{ svg "discord" }}</a>
|
<a class="svgicon ml2" href="https://discord.gg/hxWxDee" target="_blank">{{ svg "discord" }}</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
|
@ -207,7 +207,7 @@
|
||||||
<h2>Community Showcase</h2>
|
<h2>Community Showcase</h2>
|
||||||
<div class="bg--card pa3 br3">
|
<div class="bg--card pa3 br3">
|
||||||
<div class="mb3">
|
<div class="mb3">
|
||||||
This is a selection of recent work done by community members. Want to participate? <a href="{{ .DiscordUrl }}" target="_blank">Join us on Discord.</a>
|
This is a selection of recent work done by community members. Want to participate? <a href="https://discord.gg/hxWxDee" target="_blank">Join us on Discord.</a>
|
||||||
</div>
|
</div>
|
||||||
<div id="showcase-container"></div>
|
<div id="showcase-container"></div>
|
||||||
<div>
|
<div>
|
||||||
|
|
|
@ -7,10 +7,32 @@
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
||||||
{{ define "content" }}
|
{{ define "content" }}
|
||||||
<div class="flex flex-column flex-row-l">
|
<div class="flex flex-column flex-row-ns">
|
||||||
<div class="flex-grow-1 overflow-hidden">
|
<div class="sidebar flex-shrink-0 mw5 self-center self-start-ns mh3 ml0-ns overflow-hidden">
|
||||||
|
<img alt="{{ .Project.Name }}" class="br3" src="{{ .Project.Logo }}" />
|
||||||
|
<a href="{{ .CurrentProjectUrl }}">
|
||||||
|
<h2 class="mt3">{{ .Project.Name }}</h2>
|
||||||
|
</a>
|
||||||
|
<div class="mt3">
|
||||||
|
<div class="mb3">
|
||||||
|
{{ range $i, $owner := .Owners }}
|
||||||
|
<div class="flex mv3 items-center {{ if eq $i 0 }}mt2{{ end }}">
|
||||||
|
<img class="avatar-icon mr2" src="{{ $owner.AvatarUrl }}" />
|
||||||
|
<a class="user-link" href="{{ $owner.ProfileUrl }}">{{ $owner.Name }}</a>
|
||||||
|
</div>
|
||||||
|
{{ end }}
|
||||||
|
</div>
|
||||||
|
{{ range .ProjectLinks }}
|
||||||
|
<div class="pair flex">
|
||||||
|
<div class="key flex-auto flex-shrink-0 mr2">{{ .Name }}</div>
|
||||||
|
<div class="value projectlink truncate"><a class="external" href="{{ .Url }}" ><span class="icon-{{ .Icon }}"></span> {{ .LinkText }}</a></div>
|
||||||
|
</div>
|
||||||
|
{{ end }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex-grow-1 overflow-hidden">
|
||||||
{{ with .Screenshots }}
|
{{ with .Screenshots }}
|
||||||
<div class="carousel-container mw-100 mv2 mv3-ns margin-center">
|
<div class="carousel-container mw-100 mb3">
|
||||||
<div class="carousel aspect-ratio aspect-ratio--16x9 overflow-hidden bg--dim br2-ns">
|
<div class="carousel aspect-ratio aspect-ratio--16x9 overflow-hidden bg--dim br2-ns">
|
||||||
<div class="dn db-l">
|
<div class="dn db-l">
|
||||||
{{ range $index, $screenshot := . }}
|
{{ range $index, $screenshot := . }}
|
||||||
|
@ -48,27 +70,6 @@
|
||||||
</div>
|
</div>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
</div>
|
</div>
|
||||||
<div class="sidebar flex-shrink-0 mw6 w-30-l self-center self-start-l mh3 mh0-ns ml3-l overflow-hidden">
|
|
||||||
<div class="content-block">
|
|
||||||
<img alt="{{ .Project.Name }} Logo" class="br3" src="{{ .Project.Logo }}" />
|
|
||||||
<div class="mv3 relative">
|
|
||||||
<div class="mb3">
|
|
||||||
{{ range $i, $owner := .Owners }}
|
|
||||||
<div class="flex mv3 items-center {{ if eq $i 0 }}mt2{{ end }}">
|
|
||||||
<img class="avatar-icon mr2" src="{{ $owner.AvatarUrl }}" />
|
|
||||||
<a class="user-link" href="{{ $owner.ProfileUrl }}">{{ $owner.Name }}</a>
|
|
||||||
</div>
|
|
||||||
{{ end }}
|
|
||||||
</div>
|
|
||||||
{{ range .ProjectLinks }}
|
|
||||||
<div class="pair flex flex-wrap">
|
|
||||||
<div class="key flex-auto mr1">{{ .Name }}</div>
|
|
||||||
<div class="value projectlink"><a class="external" href="{{ .Url }}" ><span class="icon-{{ .Icon }}"></span> {{ .LinkText }}</a></div>
|
|
||||||
</div>
|
|
||||||
{{ end }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<script>
|
<script>
|
||||||
const numCarouselItems = {{ len .Screenshots }};
|
const numCarouselItems = {{ len .Screenshots }};
|
||||||
|
@ -89,6 +90,10 @@
|
||||||
|
|
||||||
let carouselTimerCurrent = 0;
|
let carouselTimerCurrent = 0;
|
||||||
const carouselTimer = setInterval(() => {
|
const carouselTimer = setInterval(() => {
|
||||||
|
if (numCarouselItems === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const next = (carouselTimerCurrent + 1) % numCarouselItems;
|
const next = (carouselTimerCurrent + 1) % numCarouselItems;
|
||||||
activateCarouselItem(next);
|
activateCarouselItem(next);
|
||||||
carouselTimerCurrent = next;
|
carouselTimerCurrent = next;
|
||||||
|
|
|
@ -123,6 +123,10 @@
|
||||||
|
|
||||||
let carouselTimerCurrent = 0;
|
let carouselTimerCurrent = 0;
|
||||||
const carouselTimer = setInterval(() => {
|
const carouselTimer = setInterval(() => {
|
||||||
|
if (numCarouselItems === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const next = (carouselTimerCurrent + 1) % numCarouselItems;
|
const next = (carouselTimerCurrent + 1) % numCarouselItems;
|
||||||
activateCarousel(next);
|
activateCarousel(next);
|
||||||
carouselTimerCurrent = next;
|
carouselTimerCurrent = next;
|
||||||
|
|
|
@ -16,9 +16,10 @@ type BaseData struct {
|
||||||
Notices []Notice
|
Notices []Notice
|
||||||
ReportIssueMailto string
|
ReportIssueMailto string
|
||||||
|
|
||||||
CurrentUrl string
|
CurrentUrl string
|
||||||
LoginPageUrl string
|
CurrentProjectUrl string
|
||||||
ProjectCSSUrl string
|
LoginPageUrl string
|
||||||
|
ProjectCSSUrl string
|
||||||
|
|
||||||
Project Project
|
Project Project
|
||||||
User *User
|
User *User
|
||||||
|
@ -50,6 +51,17 @@ type Header struct {
|
||||||
PodcastUrl string
|
PodcastUrl string
|
||||||
ForumsUrl string
|
ForumsUrl string
|
||||||
LibraryUrl string
|
LibraryUrl string
|
||||||
|
|
||||||
|
Project *ProjectHeader
|
||||||
|
}
|
||||||
|
|
||||||
|
type ProjectHeader struct {
|
||||||
|
HasForums bool
|
||||||
|
HasBlog bool
|
||||||
|
HasEpisodeGuide bool
|
||||||
|
ForumsUrl string
|
||||||
|
BlogUrl string
|
||||||
|
EpisodeGuideUrl string
|
||||||
}
|
}
|
||||||
|
|
||||||
type Footer struct {
|
type Footer struct {
|
||||||
|
|
|
@ -0,0 +1,125 @@
|
||||||
|
package website
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.handmade.network/hmn/hmn/src/config"
|
||||||
|
"git.handmade.network/hmn/hmn/src/hmnurl"
|
||||||
|
"git.handmade.network/hmn/hmn/src/models"
|
||||||
|
"git.handmade.network/hmn/hmn/src/templates"
|
||||||
|
)
|
||||||
|
|
||||||
|
func getBaseDataAutocrumb(c *RequestContext, title string) templates.BaseData {
|
||||||
|
return getBaseData(c, title, []templates.Breadcrumb{{Name: title, Url: ""}})
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE(asaf): If you set breadcrumbs, the breadcrumb for the current project will automatically be prepended when necessary.
|
||||||
|
// If you pass nil, no breadcrumbs will be created.
|
||||||
|
func getBaseData(c *RequestContext, title string, breadcrumbs []templates.Breadcrumb) templates.BaseData {
|
||||||
|
var templateUser *templates.User
|
||||||
|
var templateSession *templates.Session
|
||||||
|
if c.CurrentUser != nil {
|
||||||
|
u := templates.UserToTemplate(c.CurrentUser, c.Theme)
|
||||||
|
s := templates.SessionToTemplate(c.CurrentSession)
|
||||||
|
templateUser = &u
|
||||||
|
templateSession = &s
|
||||||
|
}
|
||||||
|
|
||||||
|
notices := getNoticesFromCookie(c)
|
||||||
|
|
||||||
|
if len(breadcrumbs) > 0 {
|
||||||
|
projectUrl := hmnurl.BuildProjectHomepage(c.CurrentProject.Slug)
|
||||||
|
if breadcrumbs[0].Url != projectUrl {
|
||||||
|
rootBreadcrumb := templates.Breadcrumb{
|
||||||
|
Name: c.CurrentProject.Name,
|
||||||
|
Url: projectUrl,
|
||||||
|
}
|
||||||
|
breadcrumbs = append([]templates.Breadcrumb{rootBreadcrumb}, breadcrumbs...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
baseData := templates.BaseData{
|
||||||
|
Theme: c.Theme,
|
||||||
|
Title: title,
|
||||||
|
Breadcrumbs: breadcrumbs,
|
||||||
|
|
||||||
|
CurrentUrl: c.FullUrl(),
|
||||||
|
CurrentProjectUrl: hmnurl.BuildProjectHomepage(c.CurrentProject.Slug),
|
||||||
|
LoginPageUrl: hmnurl.BuildLoginPage(c.FullUrl()),
|
||||||
|
ProjectCSSUrl: hmnurl.BuildProjectCSS(c.CurrentProject.Color1),
|
||||||
|
|
||||||
|
Project: templates.ProjectToTemplate(c.CurrentProject, c.Theme),
|
||||||
|
User: templateUser,
|
||||||
|
Session: templateSession,
|
||||||
|
Notices: notices,
|
||||||
|
|
||||||
|
ReportIssueMailto: "team@handmade.network",
|
||||||
|
|
||||||
|
OpenGraphItems: buildDefaultOpenGraphItems(c.CurrentProject, title),
|
||||||
|
|
||||||
|
IsProjectPage: !c.CurrentProject.IsHMN(),
|
||||||
|
Header: templates.Header{
|
||||||
|
AdminUrl: hmnurl.BuildAdminApprovalQueue(), // TODO(asaf): Replace with general-purpose admin page
|
||||||
|
UserSettingsUrl: hmnurl.BuildUserSettings(""),
|
||||||
|
LoginActionUrl: hmnurl.BuildLoginAction(c.FullUrl()),
|
||||||
|
LogoutActionUrl: hmnurl.BuildLogoutAction(c.FullUrl()),
|
||||||
|
ForgotPasswordUrl: hmnurl.BuildRequestPasswordReset(),
|
||||||
|
RegisterUrl: hmnurl.BuildRegister(),
|
||||||
|
|
||||||
|
HMNHomepageUrl: hmnurl.BuildHomepage(),
|
||||||
|
ProjectIndexUrl: hmnurl.BuildProjectIndex(1),
|
||||||
|
PodcastUrl: hmnurl.BuildPodcast(),
|
||||||
|
ForumsUrl: hmnurl.BuildForum(models.HMNProjectSlug, nil, 1),
|
||||||
|
LibraryUrl: hmnurl.BuildLibrary(),
|
||||||
|
},
|
||||||
|
Footer: templates.Footer{
|
||||||
|
HomepageUrl: hmnurl.BuildHomepage(),
|
||||||
|
AboutUrl: hmnurl.BuildAbout(),
|
||||||
|
ManifestoUrl: hmnurl.BuildManifesto(),
|
||||||
|
CodeOfConductUrl: hmnurl.BuildCodeOfConduct(),
|
||||||
|
CommunicationGuidelinesUrl: hmnurl.BuildCommunicationGuidelines(),
|
||||||
|
ProjectIndexUrl: hmnurl.BuildProjectIndex(1),
|
||||||
|
ForumsUrl: hmnurl.BuildForum(models.HMNProjectSlug, nil, 1),
|
||||||
|
ContactUrl: hmnurl.BuildContactPage(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.CurrentUser != nil {
|
||||||
|
baseData.Header.UserProfileUrl = hmnurl.BuildUserProfile(c.CurrentUser.Username)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !c.CurrentProject.IsHMN() {
|
||||||
|
episodeGuideUrl := ""
|
||||||
|
defaultTopic, hasAnnotations := config.Config.EpisodeGuide.Projects[c.CurrentProject.Slug]
|
||||||
|
if hasAnnotations {
|
||||||
|
episodeGuideUrl = hmnurl.BuildEpisodeList(c.CurrentProject.Slug, defaultTopic)
|
||||||
|
}
|
||||||
|
|
||||||
|
baseData.Header.Project = &templates.ProjectHeader{
|
||||||
|
HasForums: c.CurrentProject.ForumEnabled,
|
||||||
|
HasBlog: c.CurrentProject.BlogEnabled,
|
||||||
|
HasEpisodeGuide: hasAnnotations,
|
||||||
|
ForumsUrl: hmnurl.BuildForum(c.CurrentProject.Slug, nil, 1),
|
||||||
|
BlogUrl: hmnurl.BuildBlog(c.CurrentProject.Slug, 1),
|
||||||
|
EpisodeGuideUrl: episodeGuideUrl,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return baseData
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildDefaultOpenGraphItems(project *models.Project, title string) []templates.OpenGraphItem {
|
||||||
|
if title == "" {
|
||||||
|
title = "Handmade Network"
|
||||||
|
}
|
||||||
|
|
||||||
|
image := hmnurl.BuildPublic("logo.png", false)
|
||||||
|
if !project.IsHMN() {
|
||||||
|
image = hmnurl.BuildUserFile(project.LogoLight)
|
||||||
|
}
|
||||||
|
|
||||||
|
return []templates.OpenGraphItem{
|
||||||
|
{Property: "og:title", Value: title},
|
||||||
|
{Property: "og:site_name", Value: "Handmade Network"},
|
||||||
|
{Property: "og:type", Value: "website"},
|
||||||
|
{Property: "og:image", Value: image},
|
||||||
|
}
|
||||||
|
}
|
|
@ -25,9 +25,6 @@ type LandingTemplateData struct {
|
||||||
FeedUrl string
|
FeedUrl string
|
||||||
PodcastUrl string
|
PodcastUrl string
|
||||||
StreamsUrl string
|
StreamsUrl string
|
||||||
IRCUrl string
|
|
||||||
DiscordUrl string
|
|
||||||
ShowUrl string
|
|
||||||
ShowcaseUrl string
|
ShowcaseUrl string
|
||||||
AtomFeedUrl string
|
AtomFeedUrl string
|
||||||
MarkAllReadUrl string
|
MarkAllReadUrl string
|
||||||
|
@ -168,9 +165,6 @@ func Index(c *RequestContext) ResponseData {
|
||||||
FeedUrl: hmnurl.BuildFeed(),
|
FeedUrl: hmnurl.BuildFeed(),
|
||||||
PodcastUrl: hmnurl.BuildPodcast(),
|
PodcastUrl: hmnurl.BuildPodcast(),
|
||||||
StreamsUrl: hmnurl.BuildStreams(),
|
StreamsUrl: hmnurl.BuildStreams(),
|
||||||
IRCUrl: hmnurl.BuildBlogThread(models.HMNProjectSlug, 1138, "[Tutorial] Handmade Network IRC"),
|
|
||||||
DiscordUrl: "https://discord.gg/hxWxDee",
|
|
||||||
ShowUrl: "https://handmadedev.show/",
|
|
||||||
ShowcaseUrl: hmnurl.BuildShowcase(),
|
ShowcaseUrl: hmnurl.BuildShowcase(),
|
||||||
AtomFeedUrl: hmnurl.BuildAtomFeed(),
|
AtomFeedUrl: hmnurl.BuildAtomFeed(),
|
||||||
MarkAllReadUrl: hmnurl.BuildForumMarkRead(models.HMNProjectSlug, 0),
|
MarkAllReadUrl: hmnurl.BuildForumMarkRead(models.HMNProjectSlug, 0),
|
||||||
|
|
|
@ -277,112 +277,6 @@ func NewWebsiteRoutes(longRequestContext context.Context, conn *pgxpool.Pool, pe
|
||||||
return router
|
return router
|
||||||
}
|
}
|
||||||
|
|
||||||
func getBaseDataAutocrumb(c *RequestContext, title string) templates.BaseData {
|
|
||||||
return getBaseData(c, title, []templates.Breadcrumb{{Name: title, Url: ""}})
|
|
||||||
}
|
|
||||||
|
|
||||||
// NOTE(asaf): If you set breadcrumbs, the breadcrumb for the current project will automatically be prepended when necessary.
|
|
||||||
// If you pass nil, no breadcrumbs will be created.
|
|
||||||
func getBaseData(c *RequestContext, title string, breadcrumbs []templates.Breadcrumb) templates.BaseData {
|
|
||||||
var templateUser *templates.User
|
|
||||||
var templateSession *templates.Session
|
|
||||||
if c.CurrentUser != nil {
|
|
||||||
u := templates.UserToTemplate(c.CurrentUser, c.Theme)
|
|
||||||
s := templates.SessionToTemplate(c.CurrentSession)
|
|
||||||
templateUser = &u
|
|
||||||
templateSession = &s
|
|
||||||
}
|
|
||||||
|
|
||||||
notices := getNoticesFromCookie(c)
|
|
||||||
|
|
||||||
if len(breadcrumbs) > 0 {
|
|
||||||
projectUrl := hmnurl.BuildProjectHomepage(c.CurrentProject.Slug)
|
|
||||||
if breadcrumbs[0].Url != projectUrl {
|
|
||||||
rootBreadcrumb := templates.Breadcrumb{
|
|
||||||
Name: c.CurrentProject.Name,
|
|
||||||
Url: projectUrl,
|
|
||||||
}
|
|
||||||
breadcrumbs = append([]templates.Breadcrumb{rootBreadcrumb}, breadcrumbs...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: move to project-specific navigation
|
|
||||||
// episodeGuideUrl := ""
|
|
||||||
// defaultTopic, hasAnnotations := config.Config.EpisodeGuide.Projects[c.CurrentProject.Slug]
|
|
||||||
// if hasAnnotations {
|
|
||||||
// episodeGuideUrl = hmnurl.BuildEpisodeList(c.CurrentProject.Slug, defaultTopic)
|
|
||||||
// }
|
|
||||||
|
|
||||||
baseData := templates.BaseData{
|
|
||||||
Theme: c.Theme,
|
|
||||||
Title: title,
|
|
||||||
Breadcrumbs: breadcrumbs,
|
|
||||||
|
|
||||||
CurrentUrl: c.FullUrl(),
|
|
||||||
LoginPageUrl: hmnurl.BuildLoginPage(c.FullUrl()),
|
|
||||||
ProjectCSSUrl: hmnurl.BuildProjectCSS(c.CurrentProject.Color1),
|
|
||||||
|
|
||||||
Project: templates.ProjectToTemplate(c.CurrentProject, c.Theme),
|
|
||||||
User: templateUser,
|
|
||||||
Session: templateSession,
|
|
||||||
Notices: notices,
|
|
||||||
|
|
||||||
ReportIssueMailto: "team@handmade.network",
|
|
||||||
|
|
||||||
OpenGraphItems: buildDefaultOpenGraphItems(c.CurrentProject, title),
|
|
||||||
|
|
||||||
IsProjectPage: !c.CurrentProject.IsHMN(),
|
|
||||||
Header: templates.Header{
|
|
||||||
AdminUrl: hmnurl.BuildAdminApprovalQueue(), // TODO(asaf): Replace with general-purpose admin page
|
|
||||||
UserSettingsUrl: hmnurl.BuildUserSettings(""),
|
|
||||||
LoginActionUrl: hmnurl.BuildLoginAction(c.FullUrl()),
|
|
||||||
LogoutActionUrl: hmnurl.BuildLogoutAction(c.FullUrl()),
|
|
||||||
ForgotPasswordUrl: hmnurl.BuildRequestPasswordReset(),
|
|
||||||
RegisterUrl: hmnurl.BuildRegister(),
|
|
||||||
|
|
||||||
HMNHomepageUrl: hmnurl.BuildHomepage(),
|
|
||||||
ProjectIndexUrl: hmnurl.BuildProjectIndex(1),
|
|
||||||
PodcastUrl: hmnurl.BuildPodcast(),
|
|
||||||
ForumsUrl: hmnurl.BuildForum(c.CurrentProject.Slug, nil, 1),
|
|
||||||
LibraryUrl: hmnurl.BuildLibrary(),
|
|
||||||
},
|
|
||||||
Footer: templates.Footer{
|
|
||||||
HomepageUrl: hmnurl.BuildHomepage(),
|
|
||||||
AboutUrl: hmnurl.BuildAbout(),
|
|
||||||
ManifestoUrl: hmnurl.BuildManifesto(),
|
|
||||||
CodeOfConductUrl: hmnurl.BuildCodeOfConduct(),
|
|
||||||
CommunicationGuidelinesUrl: hmnurl.BuildCommunicationGuidelines(),
|
|
||||||
ProjectIndexUrl: hmnurl.BuildProjectIndex(1),
|
|
||||||
ForumsUrl: hmnurl.BuildForum(models.HMNProjectSlug, nil, 1),
|
|
||||||
ContactUrl: hmnurl.BuildContactPage(),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.CurrentUser != nil {
|
|
||||||
baseData.Header.UserProfileUrl = hmnurl.BuildUserProfile(c.CurrentUser.Username)
|
|
||||||
}
|
|
||||||
|
|
||||||
return baseData
|
|
||||||
}
|
|
||||||
|
|
||||||
func buildDefaultOpenGraphItems(project *models.Project, title string) []templates.OpenGraphItem {
|
|
||||||
if title == "" {
|
|
||||||
title = "Handmade Network"
|
|
||||||
}
|
|
||||||
|
|
||||||
image := hmnurl.BuildPublic("logo.png", false)
|
|
||||||
if !project.IsHMN() {
|
|
||||||
image = hmnurl.BuildUserFile(project.LogoLight)
|
|
||||||
}
|
|
||||||
|
|
||||||
return []templates.OpenGraphItem{
|
|
||||||
{Property: "og:title", Value: title},
|
|
||||||
{Property: "og:site_name", Value: "Handmade Network"},
|
|
||||||
{Property: "og:type", Value: "website"},
|
|
||||||
{Property: "og:image", Value: image},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func FetchProjectBySlug(ctx context.Context, conn *pgxpool.Pool, slug string) (*models.Project, error) {
|
func FetchProjectBySlug(ctx context.Context, conn *pgxpool.Pool, slug string) (*models.Project, error) {
|
||||||
if len(slug) > 0 && slug != models.HMNProjectSlug {
|
if len(slug) > 0 && slug != models.HMNProjectSlug {
|
||||||
subdomainProjectRow, err := db.QueryOne(ctx, conn, models.Project{}, "SELECT $columns FROM handmade_project WHERE slug = $1", slug)
|
subdomainProjectRow, err := db.QueryOne(ctx, conn, models.Project{}, "SELECT $columns FROM handmade_project WHERE slug = $1", slug)
|
||||||
|
|
Loading…
Reference in New Issue