Snippet page
This commit is contained in:
parent
5162e7fba9
commit
090e484e72
|
@ -0,0 +1,27 @@
|
|||
{{ template "base.html" . }}
|
||||
|
||||
{{ define "content" }}
|
||||
<div class="content-block">
|
||||
<div class="flex flex-column pa3 mb2 br3">
|
||||
<div class="mb2 flex items-center">
|
||||
<img class="avatar-icon lite mr2" src="{{ .Snippet.OwnerAvatarUrl }}"/>
|
||||
<a class="user" href="{{ .Snippet.OwnerUrl }}">{{ .Snippet.OwnerName }}</a>
|
||||
<a class="tr" style="flex: 1 1 auto;" href="{{ .Snippet.Url }}">{{ timehtml (relativedate .Snippet.Date) .Snippet.Date }}</a>
|
||||
</div>
|
||||
<p class="mb2">{{ .Snippet.Description }}</p>
|
||||
<div>
|
||||
{{ if snippetimage .Snippet }}
|
||||
<img src="{{ .Snippet.AssetUrl }}" />
|
||||
{{ else if snippetvideo .Snippet }}
|
||||
<video src="{{ .Snippet.AssetUrl }}" preload="metadata" controls />
|
||||
{{ else if snippetaudio .Snippet }}
|
||||
<audio src="{{ .Snippet.AssetUrl }}" controls />
|
||||
{{ else if snippetyoutube .Snippet }}
|
||||
<div class="mb3 aspect-ratio aspect-ratio--16x9">
|
||||
<iframe class="aspect-ratio--object" src="https://www.youtube-nocookie.com/embed/{{ .Snippet.YoutubeID }}" allow="accelerometer; encrypted-media; gyroscope;" allowfullscreen frameborder="0" />
|
||||
</div>
|
||||
{{ end }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{ end }}
|
|
@ -246,6 +246,7 @@ type TimelineItem struct {
|
|||
Width int
|
||||
Height int
|
||||
AssetUrl string
|
||||
MimeType string
|
||||
YoutubeID string
|
||||
|
||||
Title string
|
||||
|
|
|
@ -108,6 +108,7 @@ func NewWebsiteRoutes(conn *pgxpool.Pool, perfCollector *perf.PerfCollector) htt
|
|||
mainRoutes.GET(hmnurl.RegexFeed, Feed)
|
||||
mainRoutes.GET(hmnurl.RegexAtomFeed, AtomFeed)
|
||||
mainRoutes.GET(hmnurl.RegexShowcase, Showcase)
|
||||
mainRoutes.GET(hmnurl.RegexSnippet, Snippet)
|
||||
mainRoutes.GET(hmnurl.RegexProjectIndex, ProjectIndex)
|
||||
mainRoutes.GET(hmnurl.RegexUserProfile, UserProfile)
|
||||
|
||||
|
|
|
@ -0,0 +1,117 @@
|
|||
package website
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"git.handmade.network/hmn/hmn/src/db"
|
||||
"git.handmade.network/hmn/hmn/src/models"
|
||||
"git.handmade.network/hmn/hmn/src/oops"
|
||||
"git.handmade.network/hmn/hmn/src/templates"
|
||||
)
|
||||
|
||||
type SnippetData struct {
|
||||
templates.BaseData
|
||||
Snippet templates.TimelineItem
|
||||
}
|
||||
|
||||
func Snippet(c *RequestContext) ResponseData {
|
||||
snippetId := -1
|
||||
snippetIdStr, found := c.PathParams["snippetid"]
|
||||
if found && snippetIdStr != "" {
|
||||
var err error
|
||||
if snippetId, err = strconv.Atoi(snippetIdStr); err != nil {
|
||||
return FourOhFour(c)
|
||||
}
|
||||
}
|
||||
if snippetId < 1 {
|
||||
return FourOhFour(c)
|
||||
}
|
||||
|
||||
c.Perf.StartBlock("SQL", "Fetch snippet")
|
||||
type snippetQuery struct {
|
||||
Owner models.User `db:"owner"`
|
||||
Snippet models.Snippet `db:"snippet"`
|
||||
Asset *models.Asset `db:"asset"`
|
||||
DiscordMessage *models.DiscordMessage `db:"discord_message"`
|
||||
}
|
||||
snippetQueryResult, err := db.QueryOne(c.Context(), c.Conn, snippetQuery{},
|
||||
`
|
||||
SELECT $columns
|
||||
FROM
|
||||
handmade_snippet AS snippet
|
||||
INNER JOIN auth_user AS owner ON owner.id = snippet.owner_id
|
||||
LEFT JOIN handmade_asset AS asset ON asset.id = snippet.asset_id
|
||||
LEFT JOIN handmade_discordmessage AS discord_message ON discord_message.id = snippet.discord_message_id
|
||||
WHERE snippet.id = $1
|
||||
`,
|
||||
snippetId,
|
||||
)
|
||||
if err != nil {
|
||||
if errors.Is(err, db.ErrNoMatchingRows) {
|
||||
return FourOhFour(c)
|
||||
} else {
|
||||
return ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch snippet"))
|
||||
}
|
||||
}
|
||||
c.Perf.EndBlock()
|
||||
|
||||
snippetData := snippetQueryResult.(*snippetQuery)
|
||||
|
||||
snippet := SnippetToTimelineItem(&snippetData.Snippet, snippetData.Asset, snippetData.DiscordMessage, &snippetData.Owner, c.Theme)
|
||||
|
||||
opengraph := []templates.OpenGraphItem{
|
||||
templates.OpenGraphItem{Property: "og:site_name", Value: "Handmade.Network"},
|
||||
templates.OpenGraphItem{Property: "og:type", Value: "article"},
|
||||
templates.OpenGraphItem{Property: "og:url", Value: snippet.Url},
|
||||
templates.OpenGraphItem{Property: "og:title", Value: fmt.Sprintf("Snippet by %s", snippet.OwnerName)},
|
||||
templates.OpenGraphItem{Property: "og:description", Value: string(snippet.Description)},
|
||||
}
|
||||
|
||||
if snippet.Type == templates.TimelineTypeSnippetImage {
|
||||
opengraphImage := []templates.OpenGraphItem{
|
||||
templates.OpenGraphItem{Property: "og:image", Value: snippet.AssetUrl},
|
||||
templates.OpenGraphItem{Property: "og:image:width", Value: strconv.Itoa(snippet.Width)},
|
||||
templates.OpenGraphItem{Property: "og:image:height", Value: strconv.Itoa(snippet.Height)},
|
||||
templates.OpenGraphItem{Property: "og:image:type", Value: snippet.MimeType},
|
||||
templates.OpenGraphItem{Name: "twitter:card", Value: "summary_large_image"},
|
||||
}
|
||||
opengraph = append(opengraph, opengraphImage...)
|
||||
} else if snippet.Type == templates.TimelineTypeSnippetVideo {
|
||||
opengraphVideo := []templates.OpenGraphItem{
|
||||
templates.OpenGraphItem{Property: "og:video", Value: snippet.AssetUrl},
|
||||
templates.OpenGraphItem{Property: "og:video:width", Value: strconv.Itoa(snippet.Width)},
|
||||
templates.OpenGraphItem{Property: "og:video:height", Value: strconv.Itoa(snippet.Height)},
|
||||
templates.OpenGraphItem{Property: "og:video:type", Value: snippet.MimeType},
|
||||
templates.OpenGraphItem{Name: "twitter:card", Value: "player"},
|
||||
}
|
||||
opengraph = append(opengraph, opengraphVideo...)
|
||||
} else if snippet.Type == templates.TimelineTypeSnippetAudio {
|
||||
opengraphAudio := []templates.OpenGraphItem{
|
||||
templates.OpenGraphItem{Property: "og:audio", Value: snippet.AssetUrl},
|
||||
templates.OpenGraphItem{Property: "og:audio:type", Value: snippet.MimeType},
|
||||
templates.OpenGraphItem{Name: "twitter:card", Value: "player"},
|
||||
}
|
||||
opengraph = append(opengraph, opengraphAudio...)
|
||||
} else if snippet.Type == templates.TimelineTypeSnippetYoutube {
|
||||
opengraphYoutube := []templates.OpenGraphItem{
|
||||
templates.OpenGraphItem{Property: "og:video", Value: fmt.Sprintf("https://youtube.com/watch?v=%s", snippet.YoutubeID)},
|
||||
templates.OpenGraphItem{Name: "twitter:card", Value: "player"},
|
||||
}
|
||||
opengraph = append(opengraph, opengraphYoutube...)
|
||||
}
|
||||
|
||||
baseData := getBaseData(c)
|
||||
baseData.OpenGraphItems = opengraph
|
||||
var res ResponseData
|
||||
err = res.WriteTemplate("snippet.html", SnippetData{
|
||||
BaseData: baseData,
|
||||
Snippet: snippet,
|
||||
}, c.Perf)
|
||||
if err != nil {
|
||||
return ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to render snippet template"))
|
||||
}
|
||||
return res
|
||||
}
|
|
@ -118,6 +118,7 @@ func SnippetToTimelineItem(snippet *models.Snippet, asset *models.Asset, discord
|
|||
itemType := templates.TimelineTypeUnknown
|
||||
youtubeId := ""
|
||||
assetUrl := ""
|
||||
mimeType := ""
|
||||
width := 0
|
||||
height := 0
|
||||
discordMessageUrl := ""
|
||||
|
@ -142,6 +143,7 @@ func SnippetToTimelineItem(snippet *models.Snippet, asset *models.Asset, discord
|
|||
itemType = templates.TimelineTypeSnippetAudio
|
||||
}
|
||||
assetUrl = hmnurl.BuildS3Asset(asset.S3Key)
|
||||
mimeType = asset.MimeType
|
||||
width = asset.Width
|
||||
height = asset.Height
|
||||
}
|
||||
|
@ -165,6 +167,7 @@ func SnippetToTimelineItem(snippet *models.Snippet, asset *models.Asset, discord
|
|||
Width: width,
|
||||
Height: height,
|
||||
AssetUrl: assetUrl,
|
||||
MimeType: mimeType,
|
||||
YoutubeID: youtubeId,
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue