Skip to content

Commit d05fa7b

Browse files
authored
feat(web/og): prefer file name over id in previews (#309)
1 parent 2e8244b commit d05fa7b

4 files changed

Lines changed: 75 additions & 6 deletions

File tree

internal/opengraph/opengraph.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ func abbreviate(s string, max int) string {
9696
// FileInfo contains the metadata displayed on an OG image.
9797
type FileInfo struct {
9898
ID string
99+
Name string
99100
Type string
100101
Size uint64
101102
UpdatedAt time.Time
@@ -172,8 +173,12 @@ func (r *Renderer) WriteImage(w io.Writer, info *FileInfo) error {
172173
color color.NRGBA
173174
}
174175

176+
identifierKey, identifier := "id", info.ID
177+
if info.Name != "" {
178+
identifierKey, identifier = "name", info.Name
179+
}
175180
props := []struct{ key, value string }{
176-
{"id", abbreviate(info.ID, 26)},
181+
{identifierKey, abbreviate(identifier, 26)},
177182
{"type", strings.ToLower(info.Type)},
178183
{"size", humanize.Bytes(info.Size)},
179184
}

internal/web/service_test.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -411,6 +411,53 @@ func (suite *HTTPServiceSuite) TestFileMarkdownAccept() {
411411
})
412412
}
413413

414+
func (suite *HTTPServiceSuite) TestFilePreviewMetadata() {
415+
ts := httptest.NewServer(suite.service.Handler)
416+
defer ts.Close()
417+
418+
tests := []struct {
419+
name string
420+
fileID string
421+
fileName string
422+
previewName string
423+
}{
424+
{name: "named file", fileID: "namedpreview", fileName: "my-notes", previewName: "my-notes"},
425+
{name: "unnamed file", fileID: "previewtest", previewName: "previewtest"},
426+
}
427+
428+
for _, tt := range tests {
429+
suite.Run(tt.name, func() {
430+
file := testutil.Fixtures.File(suite.T())
431+
file.ID = tt.fileID
432+
file.Name = tt.fileName
433+
file.Type = "go"
434+
435+
suite.mockDB.Files.EXPECT().Find(mock.Anything, file.ID).Return(&file, nil)
436+
suite.mockDB.Files.EXPECT().FindContent(mock.Anything, file.ID).Return([]byte("package preview"), nil)
437+
suite.mockDB.Revisions.EXPECT().CountByFileID(mock.Anything, file.ID).Return(int64(0), nil)
438+
439+
resp, err := ts.Client().Get(ts.URL + "/f/" + file.ID)
440+
suite.Require().NoError(err)
441+
defer resp.Body.Close()
442+
suite.Require().Equal(http.StatusOK, resp.StatusCode)
443+
444+
body, err := io.ReadAll(resp.Body)
445+
suite.Require().NoError(err)
446+
html := string(body)
447+
suite.Contains(html, "<title>"+tt.previewName+" - snips.sh</title>")
448+
suite.Contains(html, `property="og:title" content="`+tt.previewName+` - snips.sh"`)
449+
suite.Contains(html, `name="twitter:title" content="`+tt.previewName+` - snips.sh"`)
450+
suite.Contains(html, `property="og:description" content="`+tt.previewName+` · go · 100 B ·`)
451+
previewPath := "/f/" + file.ID
452+
if file.Name != "" {
453+
previewPath += "/n/" + file.Name
454+
}
455+
suite.Contains(html, `property="og:url" content="http://localhost:8080`+previewPath+`"`)
456+
suite.Contains(html, `property="og:image" content="http://localhost:8080`+previewPath+`/og.png"`)
457+
})
458+
}
459+
}
460+
414461
func (suite *HTTPServiceSuite) TestPprofEndpoints() {
415462
suite.Run("pprof unavailable when debug is off", func() {
416463
// Default config has Debug=false, so the route is not registered.

internal/web/ui.go

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,13 @@ func filePath(r *http.Request, file *snips.File) string {
224224
return fmt.Sprintf("/f/%s", file.ID)
225225
}
226226

227+
func preferredFilePath(file *snips.File) string {
228+
if file.Name != "" {
229+
return fmt.Sprintf("/f/%s/n/%s", file.ID, file.Name)
230+
}
231+
return fmt.Sprintf("/f/%s", file.ID)
232+
}
233+
227234
func (ui *UI) File(w http.ResponseWriter, r *http.Request) {
228235
log := logger.From(r.Context())
229236

@@ -316,12 +323,18 @@ func (ui *UI) File(w http.ResponseWriter, r *http.Request) {
316323
}
317324

318325
path := filePath(r, file)
319-
ogImageURL := fmt.Sprintf("%s://%s%s/og.png", ui.cfg.HTTP.External.Scheme, ui.cfg.HTTP.External.Host, path)
320-
ogDescription := fmt.Sprintf("%s · %s · %s · %s", file.ID, strings.ToLower(file.Type), humanize.Bytes(file.Size), humanize.Time(file.UpdatedAt))
326+
previewURL := fmt.Sprintf("%s://%s%s", ui.cfg.HTTP.External.Scheme, ui.cfg.HTTP.External.Host, preferredFilePath(file))
327+
ogImageURL := previewURL + "/og.png"
328+
previewName := file.ID
329+
if file.Name != "" {
330+
previewName = file.Name
331+
}
332+
ogDescription := fmt.Sprintf("%s · %s · %s · %s", previewName, strings.ToLower(file.Type), humanize.Bytes(file.Size), humanize.Time(file.UpdatedAt))
321333

322334
vars := map[string]interface{}{
323335
"FileID": file.ID,
324336
"FileName": file.Name,
337+
"PreviewName": previewName,
325338
"FilePath": path,
326339
"FileSize": humanize.Bytes(file.Size),
327340
"CreatedAt": humanize.Time(file.CreatedAt),
@@ -334,6 +347,7 @@ func (ui *UI) File(w http.ResponseWriter, r *http.Request) {
334347
"Private": file.Private,
335348
"CommitSHA": config.BuildCommit(),
336349
"OGImageURL": ogImageURL,
350+
"OGURL": previewURL,
337351
"OGDescription": ogDescription,
338352
"RevisionCount": revisionCount,
339353
}
@@ -402,6 +416,7 @@ func (ui *UI) OGImage(w http.ResponseWriter, r *http.Request) {
402416
var img bytes.Buffer
403417
err = ui.og.WriteImage(&img, &opengraph.FileInfo{
404418
ID: file.ID,
419+
Name: file.Name,
405420
Type: file.Type,
406421
Size: file.Size,
407422
UpdatedAt: file.UpdatedAt,

web/templates/file.go.html

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
1-
{{ define "title" }}{{ .FileID }} - snips.sh{{ end }} {{ define "head" }}
2-
<meta property="og:title" content="{{.FileID}} - snips.sh" />
1+
{{ define "title" }}{{ .PreviewName }} - snips.sh{{ end }} {{ define "head" }}
2+
<meta property="og:title" content="{{.PreviewName}} - snips.sh" />
33
<meta property="og:type" content="article" />
44
<meta property="og:site_name" content="snips.sh" />
5+
<meta property="og:url" content="{{.OGURL}}" />
56
<meta property="og:description" content="{{.OGDescription}}" />
7+
<link rel="canonical" href="{{.OGURL}}" />
68
{{ if .OGImageURL }}
79
<meta property="og:image" content="{{.OGImageURL}}" />
810
{{ end }}
911
<meta name="twitter:card" content="summary_large_image" />
10-
<meta name="twitter:title" content="{{.FileID}} - snips.sh" />
12+
<meta name="twitter:title" content="{{.PreviewName}} - snips.sh" />
1113
<meta name="twitter:description" content="{{.OGDescription}}" />
1214
{{ if .OGImageURL }}
1315
<meta name="twitter:image" content="{{.OGImageURL}}" />

0 commit comments

Comments
 (0)