feat: Add file thumbnails and fix download page layout
- Images show actual thumbnail from R2 (presigned inline URL) - Video/audio/PDF show type-specific icons - Use flexbox gap for consistent spacing (no overlap) - Move filename into card as h2, remove top-level h1 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
df182ca09f
commit
9ab1cb3349
|
|
@ -57,16 +57,35 @@ func (h *Handler) Download(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
}
|
||||
|
||||
// Determine thumbnail type
|
||||
thumbType := "" // "image", "video", "audio", "pdf", or ""
|
||||
thumbnailURL := ""
|
||||
if strings.HasPrefix(rec.ContentType, "image/") {
|
||||
thumbType = "image"
|
||||
// Generate inline presigned URL for the image thumbnail
|
||||
if url, err := h.r2.PresignGet(r.Context(), rec.R2Key, rec.Filename, true); err == nil {
|
||||
thumbnailURL = url
|
||||
}
|
||||
} else if strings.HasPrefix(rec.ContentType, "video/") {
|
||||
thumbType = "video"
|
||||
} else if strings.HasPrefix(rec.ContentType, "audio/") {
|
||||
thumbType = "audio"
|
||||
} else if rec.ContentType == "application/pdf" {
|
||||
thumbType = "pdf"
|
||||
}
|
||||
|
||||
data := map[string]any{
|
||||
"ID": rec.ID,
|
||||
"Filename": rec.Filename,
|
||||
"Size": rec.SizeBytes,
|
||||
"ContentType": rec.ContentType,
|
||||
"UploadedAt": rec.UploadedAt.Format("Jan 2, 2006 at 3:04 PM"),
|
||||
"Downloads": rec.DownloadCount,
|
||||
"Previewable": isPreviewable(rec.ContentType),
|
||||
"ViewURL": "/f/" + id + "/view",
|
||||
"DownloadURL": "/f/" + id + "/dl",
|
||||
"ID": rec.ID,
|
||||
"Filename": rec.Filename,
|
||||
"Size": rec.SizeBytes,
|
||||
"ContentType": rec.ContentType,
|
||||
"UploadedAt": rec.UploadedAt.Format("Jan 2, 2006 at 3:04 PM"),
|
||||
"Downloads": rec.DownloadCount,
|
||||
"Previewable": isPreviewable(rec.ContentType),
|
||||
"ViewURL": "/f/" + id + "/view",
|
||||
"DownloadURL": "/f/" + id + "/dl",
|
||||
"ThumbType": thumbType,
|
||||
"ThumbnailURL": thumbnailURL,
|
||||
}
|
||||
if rec.ExpiresAt != nil {
|
||||
data["ExpiresAt"] = rec.ExpiresAt.Format("Jan 2, 2006 at 3:04 PM")
|
||||
|
|
|
|||
|
|
@ -8,23 +8,45 @@
|
|||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1>{{.Filename}}</h1>
|
||||
|
||||
<div class="file-card">
|
||||
<div class="file-icon">
|
||||
{{if .Previewable}}
|
||||
<svg width="40" height="40" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
|
||||
<path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/>
|
||||
<circle cx="12" cy="12" r="3"/>
|
||||
</svg>
|
||||
<div class="file-preview">
|
||||
{{if eq .ThumbType "image"}}
|
||||
<a href="{{.ViewURL}}"><img src="{{.ThumbnailURL}}" alt="{{.Filename}}" class="thumbnail"></a>
|
||||
{{else if eq .ThumbType "video"}}
|
||||
<div class="thumb-icon">
|
||||
<svg width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
|
||||
<polygon points="5 3 19 12 5 21 5 3"/>
|
||||
</svg>
|
||||
</div>
|
||||
{{else if eq .ThumbType "audio"}}
|
||||
<div class="thumb-icon">
|
||||
<svg width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
|
||||
<path d="M9 18V5l12-2v13"/>
|
||||
<circle cx="6" cy="18" r="3"/>
|
||||
<circle cx="18" cy="16" r="3"/>
|
||||
</svg>
|
||||
</div>
|
||||
{{else if eq .ThumbType "pdf"}}
|
||||
<div class="thumb-icon">
|
||||
<svg width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
|
||||
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/>
|
||||
<polyline points="14 2 14 8 20 8"/>
|
||||
<line x1="16" y1="13" x2="8" y2="13"/>
|
||||
<line x1="16" y1="17" x2="8" y2="17"/>
|
||||
</svg>
|
||||
</div>
|
||||
{{else}}
|
||||
<svg width="40" height="40" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
|
||||
<path d="M13 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9z"/>
|
||||
<polyline points="13 2 13 9 20 9"/>
|
||||
</svg>
|
||||
<div class="thumb-icon">
|
||||
<svg width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
|
||||
<path d="M13 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9z"/>
|
||||
<polyline points="13 2 13 9 20 9"/>
|
||||
</svg>
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
|
||||
<h2 class="file-title">{{.Filename}}</h2>
|
||||
|
||||
<div class="file-meta">
|
||||
<div class="meta-row">
|
||||
<span class="meta-label">Size</span>
|
||||
|
|
|
|||
|
|
@ -224,24 +224,51 @@ footer a:hover { color: var(--accent); }
|
|||
|
||||
/* Download page */
|
||||
.file-card {
|
||||
margin-top: 1.5rem;
|
||||
margin-top: 0;
|
||||
padding: 1.5rem;
|
||||
background: var(--surface);
|
||||
border-radius: 12px;
|
||||
border: 1px solid var(--border);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1.25rem;
|
||||
}
|
||||
|
||||
.file-icon {
|
||||
.file-preview {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.thumbnail {
|
||||
max-width: 100%;
|
||||
max-height: 280px;
|
||||
border-radius: 8px;
|
||||
object-fit: contain;
|
||||
background: var(--bg);
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.thumb-icon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 120px;
|
||||
color: var(--text-dim);
|
||||
margin-bottom: 1.25rem;
|
||||
background: var(--bg);
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.file-title {
|
||||
font-size: 1rem;
|
||||
font-weight: 600;
|
||||
word-break: break-all;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.file-meta {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
margin-bottom: 1.25rem;
|
||||
}
|
||||
|
||||
.meta-row {
|
||||
|
|
|
|||
Loading…
Reference in New Issue