Compare commits

..

No commits in common. "master" and "nuke-scss" have entirely different histories.

18 changed files with 31 additions and 98 deletions

View File

@ -10,10 +10,12 @@ We want the website to be a great example of Handmade software on the web. We en
You will need the following software installed:
- Go 1.21 or newer: https://go.dev/
- Go 1.18 or 1.19: https://go.dev/
You can download Go directly from the website, or install it through major package managers. If you already have Go installed, but are unsure of the version, you can check by running `go version`.
**PLEASE NOTE:** Go 1.20 currently does not work due to a bug in a third-party library. See [this issue](https://git.handmade.network/hmn/hmn/issues/59#issuecomment-1335).
- Postgres: https://www.postgresql.org/
Any Postgres installation should work fine, although less common distributions may not work as nicely with our scripts out of the box. On Mac, [Postgres.app](https://postgresapp.com/) is recommended.

2
go.mod
View File

@ -1,6 +1,6 @@
module git.handmade.network/hmn/hmn
go 1.21
go 1.18
require (
github.com/HandmadeNetwork/golorem v0.0.0-20220507185207-414965a3a817

3
go.sum
View File

@ -119,7 +119,6 @@ github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
@ -185,7 +184,6 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxv
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
@ -427,7 +425,6 @@ gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLks
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=

View File

@ -7501,14 +7501,6 @@ video {
.breadcrumb.current {
text-overflow: clip ellipsis;
}
.aspect-ratio-real--1x1 {
aspect-ratio: 1 / 1;
}
.center-abs {
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
@media screen and (min-width: 35em) {
.bg1-ns {
background-color: var(--c1);
@ -8904,8 +8896,9 @@ code .ss,
max-height: 60vh;
}
.timeline-item .timeline-media.timeline-embed {
height: 0;
position: relative;
aspect-ratio: 16 / 9;
padding-bottom: 56.25%;
}
.timeline-item .timeline-media.timeline-embed > iframe {
position: absolute;

View File

@ -620,20 +620,5 @@ func init() {
}
adminCommand.AddCommand(uploadAsset)
adminCommand.AddCommand(&cobra.Command{
Use: "newsletteremails",
Short: "Print a list of all newsletter email receipients",
Run: func(cmd *cobra.Command, args []string) {
ctx := context.Background()
conn := db.NewConn()
defer conn.Close(ctx)
recipients := utils.Must1(db.Query[models.NewsletterEmail](ctx, conn, `SELECT $columns FROM newsletter_emails`))
for _, r := range recipients {
fmt.Println(r.Email)
}
},
})
addProjectCommands(adminCommand)
}

View File

@ -1,5 +0,0 @@
package models
type NewsletterEmail struct {
Email string `db:"email"`
}

View File

@ -363,17 +363,6 @@ on our SVGs to ensure that they naturally render at the right size.)
}
}
/* NOTE(asaf): Tachyons uses a padding trick instead of using the actual property */
.aspect-ratio-real--1x1 {
aspect-ratio: 1 / 1;
}
.center-abs {
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
@media screen and (min-width: 35em) {
.bg1-ns {
background-color: var(--c1);

View File

@ -33,10 +33,13 @@
max-height: 60vh;
&.timeline-embed {
/* aspect-ratio aspect-ratio--16x9 */
height: 0;
position: relative;
aspect-ratio: 16 / 9;
padding-bottom: 56.25%;
>iframe {
/* aspect-ratio--object */
position: absolute;
top: 0;
right: 0;

View File

@ -1 +0,0 @@
<svg stroke="currentColor" fill="currentColor" stroke-width="0" viewBox="-40 0 448 512" xmlns="http://www.w3.org/2000/svg"><path d="M424.4 214.7L72.4 6.6C43.8-10.3 0 6.1 0 47.9V464c0 37.5 40.7 60.1 72.4 41.3l352-208c31.4-18.5 31.5-64.1 0-82.6z"></path></svg>

Before

Width:  |  Height:  |  Size: 259 B

View File

@ -24,10 +24,10 @@
{{ range .Topics }}
{{ if .Url }}
<li class="ttc">
<a href="{{ .Url }}">{{ .Username }}</a>
<a href="{{ .Url }}">{{ .LinkText }}</a>
</li>
{{ else }}
<li class="ttc"><strong>{{ .Username }}</strong></li>
<li class="ttc"><strong>{{ .LinkText }}</strong></li>
{{ end }}
{{ end }}
</ul>

View File

@ -65,9 +65,6 @@
<div class="submenu right-0" id="profile-submenu">
<a href="{{ .Header.UserProfileUrl }}">Profile</a>
<a href="{{ .Header.UserSettingsUrl }}">Settings</a>
{{ if .User.IsStaff }}
<a href="{{ .Header.AdminApprovalQueueUrl }}">User approvals</a>
{{ end }}
<a href="{{ .Header.LogoutUrl }}">Log Out</a>
</div>
</div>

View File

@ -60,7 +60,7 @@
{{ range .Media }}
<div class="timeline-media mt3 {{ if eq .Type mediaembed }}timeline-embed{{ end }} overflow-hidden flex {{ if not (eq .Type mediaunknown) }}justify-center{{ end }}">
{{ if eq .Type mediaimage }}
<img decoding="async" loading="lazy" src="{{ .AssetUrl }}" {{ if and .Width .Height }}style="aspect-ratio: {{ .Width }} / {{ .Height }};"{{ end }} />
<img src="{{ .AssetUrl }}">
{{ else if eq .Type mediavideo }}
{{ if .ThumbnailUrl }}
<video src="{{ .AssetUrl }}" poster="{{ .ThumbnailUrl }}" preload="none" controls>
@ -70,19 +70,7 @@
{{ else if eq .Type mediaaudio }}
<audio src="{{ .AssetUrl }}" controls>
{{ else if eq .Type mediaembed }}
{{ if .ThumbnailUrl }}
<div class="relative" onclick="this.insertAdjacentElement('beforebegin', this.parentElement.querySelector('template').content.cloneNode(true).firstElementChild); this.remove();">
<img src="{{ .ThumbnailUrl }}" />
<div class="overflow-hidden absolute center-abs c2 br-100 bg-transparent pointer aspect-ratio-real--1x1 pa3 flex justify-center items-center">
<div class="svgicon-lite w2 flex items-center pa1">
{{ svg "play" }}
</div>
</div>
</div>
<template>{{ .EmbedHTML }}</template>
{{ else }}
{{ .EmbedHTML }}
{{ end }}
{{ .EmbedHTML }}
{{ else }}
<div class="project-card pv1 ph2">
<a href="{{ .AssetUrl }}" target="_blank">{{ .Filename }} ({{ filesize .FileSize }})</a>

View File

@ -40,13 +40,12 @@ func (bd *BaseData) AddImmediateNotice(class, content string) {
}
type Header struct {
AdminApprovalQueueUrl string
AdminUrl string // TODO(redesign): Remove this once we get rid of the old header
UserProfileUrl string
UserSettingsUrl string
LogoutUrl string
ForgotPasswordUrl string
RegisterUrl string
AdminUrl string
UserProfileUrl string
UserSettingsUrl string
LogoutUrl string
ForgotPasswordUrl string
RegisterUrl string
HMNHomepageUrl string
ProjectIndexUrl string

View File

@ -81,11 +81,11 @@ func getBaseData(c *RequestContext, title string, breadcrumbs []templates.Breadc
IsProjectPage: !project.IsHMN(),
Header: templates.Header{
AdminApprovalQueueUrl: hmnurl.BuildAdminApprovalQueue(), // TODO(asaf): Replace with general-purpose admin page
UserSettingsUrl: hmnurl.BuildUserSettings(""),
LogoutUrl: hmnurl.BuildLogoutAction(c.FullUrl()),
ForgotPasswordUrl: hmnurl.BuildRequestPasswordReset(),
RegisterUrl: hmnurl.BuildRegister(""),
AdminUrl: hmnurl.BuildAdminApprovalQueue(), // TODO(asaf): Replace with general-purpose admin page
UserSettingsUrl: hmnurl.BuildUserSettings(""),
LogoutUrl: hmnurl.BuildLogoutAction(c.FullUrl()),
ForgotPasswordUrl: hmnurl.BuildRequestPasswordReset(),
RegisterUrl: hmnurl.BuildRegister(""),
HMNHomepageUrl: hmnurl.BuildHomepage(),
ProjectIndexUrl: hmnurl.BuildProjectIndex(),

View File

@ -55,11 +55,6 @@ func EpisodeList(c *RequestContext) ResponseData {
return c.Redirect(c.UrlContext.BuildHomepage(), http.StatusSeeOther)
}
if slug == "hero" {
// NOTE(asaf): Manual override for HMH
return c.Redirect(fmt.Sprintf("https://guide.handmadehero.org/%s", topic), http.StatusSeeOther)
}
if topic == "" {
return c.Redirect(c.UrlContext.BuildEpisodeList(defaultTopic), http.StatusSeeOther)
}
@ -121,11 +116,6 @@ func Episode(c *RequestContext) ResponseData {
return c.Redirect(c.UrlContext.BuildHomepage(), http.StatusSeeOther)
}
if slug == "hero" {
// NOTE(asaf): Manual override for HMH
return c.Redirect(fmt.Sprintf("https://guide.handmadehero.org/%s/%s", topic, episode), http.StatusSeeOther)
}
_, foundTopic := topicsForProject(slug, topic)
if foundTopic == "" {
return FourOhFour(c)

View File

@ -14,8 +14,7 @@ import (
)
func Index(c *RequestContext) ResponseData {
const maxPostsPerTab = 20
const maxNewsPosts = 10
const maxPostsPerTab = 60
c.Perf.StartBlock("SQL", "Fetch subforum tree")
subforumTree := models.GetFullSubforumTree(c, c.Conn)
@ -91,8 +90,6 @@ func Index(c *RequestContext) ResponseData {
ProjectIDs: featuredProjectIDs,
OwnerIDs: featuredUserIDs,
Limit: maxPostsPerTab,
SkipPosts: true,
})
if err != nil {
c.Logger.Warn().Err(err).Msg("failed to fetch featured feed")
@ -108,7 +105,7 @@ func Index(c *RequestContext) ResponseData {
newsThreads, err := hmndata.FetchThreads(c, c.Conn, c.CurrentUser, hmndata.ThreadsQuery{
ProjectIDs: []int{models.HMNProjectID},
ThreadTypes: []models.ThreadType{models.ThreadTypeProjectBlogPost},
Limit: maxNewsPosts,
Limit: maxPostsPerTab,
OrderByCreated: true,
})
if err != nil {

View File

@ -108,7 +108,7 @@ func NewWebsiteRoutes(conn *pgxpool.Pool) http.Handler {
hmnOnly.GET(hmnurl.RegexJamsIndex, JamsIndex)
hmnOnly.GET(hmnurl.RegexJamIndex, func(c *RequestContext) ResponseData {
return c.Redirect(hmnurl.BuildJamIndex2024_Visibility(), http.StatusFound)
return c.Redirect(hmnurl.BuildJamSaveTheDate(), http.StatusFound)
})
hmnOnly.GET(hmnurl.RegexJamIndex2021, JamIndex2021)
hmnOnly.GET(hmnurl.RegexJamIndex2022, JamIndex2022)

View File

@ -366,14 +366,13 @@ func youtubeMediaItem(videoId string) templates.TimelineItemMedia {
return templates.TimelineItemMedia{
Type: templates.TimelineItemMediaTypeEmbed,
EmbedHTML: template.HTML(fmt.Sprintf(
`<iframe src="https://www.youtube-nocookie.com/embed/%s?autoplay=1" allow="autoplay; accelerometer; encrypted-media; gyroscope;" allowfullscreen frameborder="0"></iframe>`,
`<iframe src="https://www.youtube-nocookie.com/embed/%s" allow="accelerometer; encrypted-media; gyroscope;" allowfullscreen frameborder="0"></iframe>`,
template.HTMLEscapeString(videoId),
)),
ExtraOpenGraphItems: []templates.OpenGraphItem{
{Property: "og:video", Value: fmt.Sprintf("https://youtube.com/watch?v=%s", videoId)},
{Name: "twitter:card", Value: "player"},
},
ThumbnailUrl: fmt.Sprintf("https://i.ytimg.com/vi/%s/hq720.jpg", videoId),
}
}