feat: add notes field to favorites and enhance OG meta tags
Add an optional long-form "notes" text field to each favorite for reviews, thoughts, or extended descriptions. The field is stored in SQLite via a new migration (002_add_fave_notes.sql) and propagated through the entire stack: - Model: Notes field on Fave struct - Store: All SQL queries (Create, GetByID, Update, list methods, scanFaves) updated with notes column - Web handlers: Read/write notes in create, edit, update forms - API handlers: Notes in create, update, get, import request/response - Export: Notes included in both JSON and CSV exports - Import: Notes parsed from both JSON and CSV imports - Feed: Notes used as Atom feed item summary when present - Form template: New textarea between URL and image fields - Detail template: Display notes, enhanced og:description with cascade: notes (truncated) → URL → generic fallback text Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
a8f3aa6f7e
commit
485d01ce45
14 changed files with 151 additions and 71 deletions
|
|
@ -23,7 +23,7 @@ func TestFaveCRUD(t *testing.T) {
|
|||
}
|
||||
|
||||
// Create a fave.
|
||||
fave, err := faves.Create(user.ID, "Blade Runner 2049", "https://example.com", "", "public")
|
||||
fave, err := faves.Create(user.ID, "Blade Runner 2049", "https://example.com", "", "", "public")
|
||||
if err != nil {
|
||||
t.Fatalf("create fave: %v", err)
|
||||
}
|
||||
|
|
@ -44,7 +44,7 @@ func TestFaveCRUD(t *testing.T) {
|
|||
}
|
||||
|
||||
// Update.
|
||||
err = faves.Update(fave.ID, "Blade Runner 2049 (Final Cut)", "https://example.com/br2049", "", "private")
|
||||
err = faves.Update(fave.ID, "Blade Runner 2049 (Final Cut)", "https://example.com/br2049", "", "", "private")
|
||||
if err != nil {
|
||||
t.Fatalf("update fave: %v", err)
|
||||
}
|
||||
|
|
@ -107,6 +107,49 @@ func TestFaveCRUD(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestFaveNotes(t *testing.T) {
|
||||
db := testDB(t)
|
||||
users := NewUserStore(db)
|
||||
faves := NewFaveStore(db)
|
||||
|
||||
Argon2Memory = 1024
|
||||
Argon2Time = 1
|
||||
defer func() { Argon2Memory = 65536; Argon2Time = 3 }()
|
||||
|
||||
user, _ := users.Create("testuser", "password123", "user")
|
||||
|
||||
// Create with notes.
|
||||
fave, err := faves.Create(user.ID, "Film", "https://example.com", "", "En fantastisk film", "public")
|
||||
if err != nil {
|
||||
t.Fatalf("create fave with notes: %v", err)
|
||||
}
|
||||
if fave.Notes != "En fantastisk film" {
|
||||
t.Errorf("notes = %q, want %q", fave.Notes, "En fantastisk film")
|
||||
}
|
||||
|
||||
// Update notes.
|
||||
err = faves.Update(fave.ID, fave.Description, fave.URL, fave.ImagePath, "Oppdatert anmeldelse", fave.Privacy)
|
||||
if err != nil {
|
||||
t.Fatalf("update notes: %v", err)
|
||||
}
|
||||
updated, _ := faves.GetByID(fave.ID)
|
||||
if updated.Notes != "Oppdatert anmeldelse" {
|
||||
t.Errorf("updated notes = %q", updated.Notes)
|
||||
}
|
||||
|
||||
// Notes appear in list queries.
|
||||
list, _, _ := faves.ListByUser(user.ID, 10, 0)
|
||||
if len(list) != 1 || list[0].Notes != "Oppdatert anmeldelse" {
|
||||
t.Error("notes should be loaded in list queries")
|
||||
}
|
||||
|
||||
// Empty notes by default.
|
||||
fave2, _ := faves.Create(user.ID, "No notes", "", "", "", "public")
|
||||
if fave2.Notes != "" {
|
||||
t.Errorf("default notes = %q, want empty", fave2.Notes)
|
||||
}
|
||||
}
|
||||
|
||||
func TestListByTag(t *testing.T) {
|
||||
db := testDB(t)
|
||||
users := NewUserStore(db)
|
||||
|
|
@ -120,9 +163,9 @@ func TestListByTag(t *testing.T) {
|
|||
user, _ := users.Create("testuser", "password123", "user")
|
||||
|
||||
// Create two public faves with overlapping tags.
|
||||
f1, _ := faves.Create(user.ID, "Fave 1", "", "", "public")
|
||||
f2, _ := faves.Create(user.ID, "Fave 2", "", "", "public")
|
||||
f3, _ := faves.Create(user.ID, "Private Fave", "", "", "private")
|
||||
f1, _ := faves.Create(user.ID, "Fave 1", "", "", "", "public")
|
||||
f2, _ := faves.Create(user.ID, "Fave 2", "", "", "", "public")
|
||||
f3, _ := faves.Create(user.ID, "Private Fave", "", "", "", "private")
|
||||
|
||||
tags.SetFaveTags(f1.ID, []string{"music", "jazz"})
|
||||
tags.SetFaveTags(f2.ID, []string{"music", "rock"})
|
||||
|
|
@ -154,7 +197,7 @@ func TestFavePagination(t *testing.T) {
|
|||
|
||||
// Create 5 faves.
|
||||
for i := 0; i < 5; i++ {
|
||||
faves.Create(user.ID, "Fave "+string(rune('A'+i)), "", "", "public")
|
||||
faves.Create(user.ID, "Fave "+string(rune('A'+i)), "", "", "", "public")
|
||||
}
|
||||
|
||||
// Page 1 with limit 2.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue