Improve Twitter/X link handling and text formatting

- Use FxTwitter API for full note tweet text (with syndication API fallback)
- Save Twitter posts based on media content:
  - Videos → embed type (proxied video)
  - Images → image type (gallery)
  - Text-only → quote type
- Add granular preview badges: 'X VIDEO', 'X GALLERY', 'X POST'
- Preserve formatting/spacing with white-space: pre-wrap for quotes and descriptions
- Rename VideoInfo to EmbedInfo for better semantic clarity
This commit is contained in:
soup 2026-01-18 00:03:30 -05:00
parent 8a046728ef
commit 4a2cb341fa
Signed by: soup
SSH key fingerprint: SHA256:GYxje8eQkJ6HZKzVWDdyOUF1TyDiprruGhE0Ym8qYDY
7 changed files with 249 additions and 137 deletions

View file

@ -28,11 +28,9 @@ type homeItem struct {
ItemType string
EmbedHTML *string
Tags []string
ThumbnailID *int64
MediaID *int64
HasVideo bool
GalleryIDs []int64 // Additional images for multi-image embeds
ImageCount int // Total image count (1 + len(GalleryIDs))
ImageIDs []int64 // Fetched images (from URLs/embeds)
}
func (h homeContent) Render(sw *ssr.Writer) error {
@ -54,33 +52,30 @@ func (h homeContent) Render(sw *ssr.Writer) error {
<div class="grid">
{{range .Items}}
<a href="/item/{{.ID}}" class="grid-item" data-type="{{.ItemType}}">
{{if eq .ItemType "quote"}}
<div class="quote-card">
<blockquote>{{.Description}}</blockquote>
{{if .Title}}<cite> {{.Title}}</cite>{{end}}
</div>
{{else if .GalleryIDs}}
<div class="grid-item-images" data-gallery="true" data-count="{{.ImageCount}}">
<img src="/media/{{.ThumbnailID}}" alt="{{if .Title}}{{.Title}}{{else}}Image{{end}}" loading="lazy" class="active">
{{range .GalleryIDs}}<img src="/media/{{.}}" alt="Image" loading="lazy">{{end}}
</div>
<div class="gallery-indicator">{{.ImageCount}}</div>
{{else if .ThumbnailID}}
<img src="/media/{{.ThumbnailID}}" alt="{{if .Title}}{{.Title}}{{else}}Image{{end}}" loading="lazy">
{{if or .HasVideo (eq .ItemType "video")}}<div class="play-indicator"></div>{{end}}
{{else if .MediaID}}
<img src="/media/{{.MediaID}}" alt="{{if .Title}}{{.Title}}{{else}}Image{{end}}" loading="lazy">
{{if or .HasVideo (eq .ItemType "video")}}<div class="play-indicator"></div>{{end}}
{{else if eq .ItemType "embed"}}
<div class="embed-placeholder">
<span></span>
</div>
{{else}}
<div class="link-card">
{{if .Title}}<div class="link-title">{{.Title}}</div>{{end}}
{{if .LinkURL}}<div class="link-url">{{.LinkURL}}</div>{{end}}
</div>
{{end}}
{{if eq .ItemType "quote"}}
<div class="quote-card">
<blockquote>{{.Description}}</blockquote>
{{if .Title}}<cite> {{.Title}}</cite>{{end}}
</div>
{{else if .ImageIDs}}
<div class="grid-item-images"{{if gt (len .ImageIDs) 1}} data-gallery="true"{{end}} data-count="{{len .ImageIDs}}">
{{range $i, $id := .ImageIDs}}<img src="/media/{{$id}}" alt="Image" loading="lazy"{{if eq $i 0}} class="active"{{end}}>{{end}}
</div>
{{if gt (len .ImageIDs) 1}}<div class="gallery-indicator">{{len .ImageIDs}}</div>{{end}}
{{if .HasVideo}}<div class="play-indicator"></div>{{end}}
{{else if .MediaID}}
<img src="/media/{{.MediaID}}" alt="Image" loading="lazy">
{{if or .HasVideo (eq .ItemType "video")}}<div class="play-indicator"></div>{{end}}
{{else if eq .ItemType "embed"}}
<div class="embed-placeholder">
<span></span>
</div>
{{else}}
<div class="link-card">
{{if .Title}}<div class="link-title">{{.Title}}</div>{{end}}
{{if .LinkURL}}<div class="link-url">{{.LinkURL}}</div>{{end}}
</div>
{{end}}
{{if or .Title .Tags}}
<div class="item-overlay">
{{if and .Title (ne .ItemType "link") (ne .ItemType "quote")}}
@ -197,12 +192,10 @@ func HandleHome(rc *RequestContext, w http.ResponseWriter, r *http.Request) erro
}
// Get media
// Media is ordered by ID, so first "image" is the thumbnail, rest are gallery
mediaList, err := media.QFindByItemID(ctx, rc.DB, it.ID)
if err != nil {
return err
}
firstImage := true
for _, m := range mediaList {
if m.MediaType == "original" {
hi.MediaID = &m.ID
@ -210,18 +203,9 @@ func HandleHome(rc *RequestContext, w http.ResponseWriter, r *http.Request) erro
hi.HasVideo = true
}
} else if m.MediaType == "image" {
if firstImage {
hi.ThumbnailID = &m.ID
firstImage = false
} else {
hi.GalleryIDs = append(hi.GalleryIDs, m.ID)
}
hi.ImageIDs = append(hi.ImageIDs, m.ID)
}
}
// Calculate total image count (thumbnail + gallery images)
if len(hi.GalleryIDs) > 0 {
hi.ImageCount = 1 + len(hi.GalleryIDs)
}
// Also check for embed video URL
if it.EmbedVideoURL.Valid && it.EmbedVideoURL.V != "" {