Coffee journaling on ATProto (alpha) alpha.arabica.social
coffee
17
fork

Configure Feed

Select the types of activity you want to include in your feed.

fix: reduce number of popular recipes shown to 3, run formatter/go fix

+122 -74
+1 -1
internal/atproto/cache_test.go
··· 1 1 package atproto 2 2 3 3 import ( 4 - "tangled.org/arabica.social/arabica/internal/models" 5 4 "sync" 5 + "tangled.org/arabica.social/arabica/internal/models" 6 6 "testing" 7 7 "time" 8 8
+2 -4
internal/firehose/profile_watcher.go
··· 71 71 // Start begins the profile watcher in a background goroutine. It will reconnect 72 72 // automatically on failure, rotating through endpoints with exponential backoff. 73 73 func (pw *ProfileWatcher) Start(ctx context.Context) { 74 - pw.wg.Add(1) 75 - go func() { 76 - defer pw.wg.Done() 74 + pw.wg.Go(func() { 77 75 pw.run(ctx) 78 - }() 76 + }) 79 77 } 80 78 81 79 // Stop gracefully shuts down the watcher.
+3 -3
internal/handlers/recipe.go
··· 782 782 return si > sj 783 783 }) 784 784 785 - // Take top 6 786 - if len(recipes) > 6 { 787 - recipes = recipes[:6] 785 + const maxRecipes = 3 786 + if len(recipes) > maxRecipes { 787 + recipes = recipes[:maxRecipes] 788 788 } 789 789 790 790 if err := components.PopularRecipes(components.PopularRecipesProps{
+1 -4
internal/ogcard/entities.go
··· 9 9 10 10 // entityStartY computes the starting Y to vertically center contentH within the card. 11 11 func entityStartY(contentH int) int { 12 - y := (brandBarY - contentH) / 2 13 - if y < 30 { 14 - y = 30 15 - } 12 + y := max((brandBarY-contentH)/2, 30) 16 13 return y 17 14 } 18 15
+1 -1
internal/web/bff/helpers.go
··· 3 3 package bff 4 4 5 5 import ( 6 - "tangled.org/arabica.social/arabica/internal/models" 7 6 "encoding/json" 8 7 "fmt" 9 8 "net/url" 10 9 "strings" 10 + "tangled.org/arabica.social/arabica/internal/models" 11 11 "time" 12 12 ) 13 13
+1 -1
internal/web/bff/helpers_test.go
··· 4 4 "testing" 5 5 "time" 6 6 7 - "tangled.org/arabica.social/arabica/internal/models" 8 7 "github.com/stretchr/testify/assert" 8 + "tangled.org/arabica.social/arabica/internal/models" 9 9 ) 10 10 11 11 func TestFormatTemp(t *testing.T) {
+1 -1
internal/web/components/brew_list_table.templ
··· 1 1 package components 2 2 3 3 import ( 4 + "fmt" 4 5 "tangled.org/arabica.social/arabica/internal/models" 5 6 "tangled.org/arabica.social/arabica/internal/web/bff" 6 - "fmt" 7 7 ) 8 8 9 9 // BrewListTableProps defines props for the brew list partial
+1 -1
internal/web/components/comments.templ
··· 1 1 package components 2 2 3 3 import ( 4 + "fmt" 4 5 "tangled.org/arabica.social/arabica/internal/firehose" 5 6 "tangled.org/arabica.social/arabica/internal/web/bff" 6 - "fmt" 7 7 ) 8 8 9 9 // CommentButtonProps defines properties for the comment button
+43 -22
internal/web/components/dialog_modals.templ
··· 1 1 package components 2 2 3 3 import ( 4 - "tangled.org/arabica.social/arabica/internal/models" 5 4 "fmt" 6 5 "strings" 6 + "tangled.org/arabica.social/arabica/internal/models" 7 7 ) 8 8 9 9 // DialogModalProps defines properties for a native HTML5 dialog modal ··· 491 491 class="w-full form-select" 492 492 > 493 493 <option value="">Select type...</option> 494 - <option value="pourover" if getStringValue(brewer, "brewer_type") == "pourover" || models.NormalizeBrewerType(getStringValue(brewer, "brewer_type")) == "pourover" { 495 - selected 496 - }>Pour-over</option> 497 - <option value="espresso" if getStringValue(brewer, "brewer_type") == "espresso" || models.NormalizeBrewerType(getStringValue(brewer, "brewer_type")) == "espresso" { 498 - selected 499 - }>Espresso</option> 500 - <option value="immersion" if getStringValue(brewer, "brewer_type") == "immersion" || models.NormalizeBrewerType(getStringValue(brewer, "brewer_type")) == "immersion" { 501 - selected 502 - }>Immersion</option> 503 - <option value="mokapot" if getStringValue(brewer, "brewer_type") == "mokapot" || models.NormalizeBrewerType(getStringValue(brewer, "brewer_type")) == "mokapot" { 504 - selected 505 - }>Moka Pot</option> 506 - <option value="coldbrew" if getStringValue(brewer, "brewer_type") == "coldbrew" || models.NormalizeBrewerType(getStringValue(brewer, "brewer_type")) == "coldbrew" { 507 - selected 508 - }>Cold Brew</option> 509 - <option value="cupping" if getStringValue(brewer, "brewer_type") == "cupping" || models.NormalizeBrewerType(getStringValue(brewer, "brewer_type")) == "cupping" { 510 - selected 511 - }>Cupping</option> 512 - <option value="other" if getStringValue(brewer, "brewer_type") == "other" || models.NormalizeBrewerType(getStringValue(brewer, "brewer_type")) == "other" { 513 - selected 514 - }>Other</option> 494 + <option 495 + value="pourover" 496 + if getStringValue(brewer, "brewer_type") == "pourover" || models.NormalizeBrewerType(getStringValue(brewer, "brewer_type")) == "pourover" { 497 + selected 498 + } 499 + >Pour-over</option> 500 + <option 501 + value="espresso" 502 + if getStringValue(brewer, "brewer_type") == "espresso" || models.NormalizeBrewerType(getStringValue(brewer, "brewer_type")) == "espresso" { 503 + selected 504 + } 505 + >Espresso</option> 506 + <option 507 + value="immersion" 508 + if getStringValue(brewer, "brewer_type") == "immersion" || models.NormalizeBrewerType(getStringValue(brewer, "brewer_type")) == "immersion" { 509 + selected 510 + } 511 + >Immersion</option> 512 + <option 513 + value="mokapot" 514 + if getStringValue(brewer, "brewer_type") == "mokapot" || models.NormalizeBrewerType(getStringValue(brewer, "brewer_type")) == "mokapot" { 515 + selected 516 + } 517 + >Moka Pot</option> 518 + <option 519 + value="coldbrew" 520 + if getStringValue(brewer, "brewer_type") == "coldbrew" || models.NormalizeBrewerType(getStringValue(brewer, "brewer_type")) == "coldbrew" { 521 + selected 522 + } 523 + >Cold Brew</option> 524 + <option 525 + value="cupping" 526 + if getStringValue(brewer, "brewer_type") == "cupping" || models.NormalizeBrewerType(getStringValue(brewer, "brewer_type")) == "cupping" { 527 + selected 528 + } 529 + >Cupping</option> 530 + <option 531 + value="other" 532 + if getStringValue(brewer, "brewer_type") == "other" || models.NormalizeBrewerType(getStringValue(brewer, "brewer_type")) == "other" { 533 + selected 534 + } 535 + >Other</option> 515 536 </select> 516 537 </div> 517 538 <div class="form-divider"></div>
+1 -1
internal/web/components/entity_tables.templ
··· 1 1 package components 2 2 3 3 import ( 4 + "fmt" 4 5 "tangled.org/arabica.social/arabica/internal/atproto" 5 6 "tangled.org/arabica.social/arabica/internal/models" 6 7 "tangled.org/arabica.social/arabica/internal/web/bff" 7 - "fmt" 8 8 "time" 9 9 ) 10 10
+2 -2
internal/web/components/header.templ
··· 1 1 package components 2 2 3 3 import ( 4 - "tangled.org/arabica.social/arabica/internal/web/bff" 5 4 "fmt" 5 + "tangled.org/arabica.social/arabica/internal/web/bff" 6 6 ) 7 7 8 8 // HeaderProps contains all properties for the header component ··· 35 35 <div class="flex items-center gap-4"> 36 36 if !props.IsAuthenticated { 37 37 <div class="flex items-center gap-4"> 38 - <button 38 + <button 39 39 x-data 40 40 @click="$dispatch('open-login')" 41 41 class="text-sm font-semibold text-brown-100 hover:text-white transition-colors"
+1 -1
internal/web/components/incomplete_records.templ
··· 1 1 package components 2 2 3 3 import ( 4 - "tangled.org/arabica.social/arabica/internal/models" 5 4 "fmt" 6 5 "strings" 6 + "tangled.org/arabica.social/arabica/internal/models" 7 7 ) 8 8 9 9 // IncompleteRecord represents a single entity that needs attention
+1 -1
internal/web/components/popular_recipes.templ
··· 1 1 package components 2 2 3 3 import ( 4 - "tangled.org/arabica.social/arabica/internal/models" 5 4 "fmt" 5 + "tangled.org/arabica.social/arabica/internal/models" 6 6 ) 7 7 8 8 // PopularRecipesProps defines the data for the popular recipes section
+1 -1
internal/web/components/profile_brew_card.templ
··· 1 1 package components 2 2 3 3 import ( 4 + "fmt" 4 5 "tangled.org/arabica.social/arabica/internal/atproto" 5 6 "tangled.org/arabica.social/arabica/internal/models" 6 7 "tangled.org/arabica.social/arabica/internal/web/bff" 7 - "fmt" 8 8 ) 9 9 10 10 // ProfileBrewCardProps defines props for a single brew card on profile
+1 -1
internal/web/components/profile_partial.templ
··· 1 1 package components 2 2 3 3 import ( 4 + "strconv" 4 5 "tangled.org/arabica.social/arabica/internal/atproto" 5 6 "tangled.org/arabica.social/arabica/internal/models" 6 - "strconv" 7 7 ) 8 8 9 9 // ProfileContentPartialProps defines props for profile content partial
+1 -1
internal/web/components/record_brew.templ
··· 1 1 package components 2 2 3 3 import ( 4 + "fmt" 4 5 "tangled.org/arabica.social/arabica/internal/models" 5 6 "tangled.org/arabica.social/arabica/internal/web/bff" 6 - "fmt" 7 7 ) 8 8 9 9 // BrewContent renders brew details inside a card
+2 -2
internal/web/pages/about_test.go
··· 1 1 package pages 2 2 3 3 import ( 4 + "bytes" 5 + "context" 4 6 "tangled.org/arabica.social/arabica/internal/web/bff" 5 7 "tangled.org/arabica.social/arabica/internal/web/components" 6 - "bytes" 7 - "context" 8 8 "testing" 9 9 10 10 "github.com/stretchr/testify/assert"
+1 -1
internal/web/pages/admin.templ
··· 1 1 package pages 2 2 3 3 import ( 4 + "fmt" 4 5 "tangled.org/arabica.social/arabica/internal/database/boltstore" 5 6 "tangled.org/arabica.social/arabica/internal/moderation" 6 7 "tangled.org/arabica.social/arabica/internal/web/bff" 7 8 "tangled.org/arabica.social/arabica/internal/web/components" 8 - "fmt" 9 9 ) 10 10 11 11 // EnrichedReport wraps a report with resolved profile info
+2 -2
internal/web/pages/bean_view.templ
··· 1 1 package pages 2 2 3 3 import ( 4 + "fmt" 5 + "strings" 4 6 "tangled.org/arabica.social/arabica/internal/firehose" 5 7 "tangled.org/arabica.social/arabica/internal/models" 6 8 "tangled.org/arabica.social/arabica/internal/web/bff" 7 9 "tangled.org/arabica.social/arabica/internal/web/components" 8 - "fmt" 9 - "strings" 10 10 ) 11 11 12 12 type BeanViewProps struct {
+19 -7
internal/web/pages/brew_view.templ
··· 1 1 package pages 2 2 3 3 import ( 4 + "fmt" 4 5 "tangled.org/arabica.social/arabica/internal/firehose" 5 6 "tangled.org/arabica.social/arabica/internal/models" 6 7 "tangled.org/arabica.social/arabica/internal/web/bff" 7 8 "tangled.org/arabica.social/arabica/internal/web/components" 8 - "fmt" 9 9 ) 10 10 11 11 // BrewViewProps defines the data for the brew view page ··· 134 134 // BrewBeanSection renders the coffee bean information 135 135 templ BrewBeanSection(brew *models.Brew, owner string) { 136 136 <div class="section-box"> 137 - <h3 class="text-sm font-medium text-brown-600 uppercase tracking-wider mb-2"><span class="inline-flex items-center gap-1">@components.IconCoffee() 138 - Coffee Bean</span></h3> 137 + <h3 class="text-sm font-medium text-brown-600 uppercase tracking-wider mb-2"> 138 + <span class="inline-flex items-center gap-1"> 139 + @components.IconCoffee() 140 + Coffee Bean 141 + </span> 142 + </h3> 139 143 if brew.Bean != nil { 140 144 <div class="font-bold text-lg text-brown-900"> 141 145 <a href={ templ.SafeURL(fmt.Sprintf("/beans/%s?owner=%s", brew.Bean.RKey, owner)) } class="hover:underline"> ··· 411 415 // BrewPoursSection renders the pours section 412 416 templ BrewPoursSection(pours []*models.Pour) { 413 417 <div class="section-box"> 414 - <h3 class="text-sm font-medium text-brown-600 uppercase tracking-wider mb-3"><span class="inline-flex items-center gap-1">@components.IconDroplet() 415 - Pours</span></h3> 418 + <h3 class="text-sm font-medium text-brown-600 uppercase tracking-wider mb-3"> 419 + <span class="inline-flex items-center gap-1"> 420 + @components.IconDroplet() 421 + Pours 422 + </span> 423 + </h3> 416 424 <div class="space-y-2"> 417 425 for _, pour := range pours { 418 426 <div class="flex justify-between items-center bg-white p-3 rounded-lg border border-brown-200"> ··· 430 438 // BrewTastingNotes renders the tasting notes section 431 439 templ BrewTastingNotes(notes string) { 432 440 <div class="section-box"> 433 - <h3 class="text-sm font-medium text-brown-600 uppercase tracking-wider mb-2"><span class="inline-flex items-center gap-1">@components.IconFileText() 434 - Tasting Notes</span></h3> 441 + <h3 class="text-sm font-medium text-brown-600 uppercase tracking-wider mb-2"> 442 + <span class="inline-flex items-center gap-1"> 443 + @components.IconFileText() 444 + Tasting Notes 445 + </span> 446 + </h3> 435 447 <div class="text-brown-900 whitespace-pre-wrap">{ notes }</div> 436 448 </div> 437 449 }
+6 -2
internal/web/pages/brewer_view.templ
··· 45 45 @components.DetailField(components.DetailFieldProps{Icon: components.IconCoffee(), Label: "Type", Value: props.Brewer.BrewerType}) 46 46 if props.Brewer.Description != "" { 47 47 <div class="section-box"> 48 - <h3 class="text-sm font-medium text-brown-600 uppercase tracking-wider mb-2"><span class="inline-flex items-center gap-1">@components.IconFileText() 49 - Description</span></h3> 48 + <h3 class="text-sm font-medium text-brown-600 uppercase tracking-wider mb-2"> 49 + <span class="inline-flex items-center gap-1"> 50 + @components.IconFileText() 51 + Description 52 + </span> 53 + </h3> 50 54 <div class="text-brown-900 whitespace-pre-wrap">{ props.Brewer.Description }</div> 51 55 </div> 52 56 }
+1 -1
internal/web/pages/components_test.go
··· 1 1 package pages 2 2 3 3 import ( 4 - "tangled.org/arabica.social/arabica/internal/web/components" 5 4 "context" 6 5 "strings" 6 + "tangled.org/arabica.social/arabica/internal/web/components" 7 7 "testing" 8 8 9 9 "github.com/a-h/templ"
+1 -1
internal/web/pages/feed.templ
··· 1 1 package pages 2 2 3 3 import ( 4 + "fmt" 4 5 "tangled.org/arabica.social/arabica/internal/feed" 5 6 "tangled.org/arabica.social/arabica/internal/lexicons" 6 7 "tangled.org/arabica.social/arabica/internal/web/bff" 7 8 "tangled.org/arabica.social/arabica/internal/web/components" 8 - "fmt" 9 9 ) 10 10 11 11 // FeedModerationContext holds moderation state for rendering feed items
+6 -2
internal/web/pages/grinder_view.templ
··· 48 48 </div> 49 49 if props.Grinder.Notes != "" { 50 50 <div class="section-box"> 51 - <h3 class="text-sm font-medium text-brown-600 uppercase tracking-wider mb-2"><span class="inline-flex items-center gap-1">@components.IconFileText() 52 - Notes</span></h3> 51 + <h3 class="text-sm font-medium text-brown-600 uppercase tracking-wider mb-2"> 52 + <span class="inline-flex items-center gap-1"> 53 + @components.IconFileText() 54 + Notes 55 + </span> 56 + </h3> 53 57 <div class="text-brown-900 whitespace-pre-wrap">{ props.Grinder.Notes }</div> 54 58 </div> 55 59 }
+1 -1
internal/web/pages/recipe_explore.templ
··· 1 1 package pages 2 2 3 3 import ( 4 + "fmt" 4 5 "tangled.org/arabica.social/arabica/internal/models" 5 6 "tangled.org/arabica.social/arabica/internal/web/components" 6 - "fmt" 7 7 ) 8 8 9 9 // RecipeExploreProps defines the data for the recipe explore page
+13 -5
internal/web/pages/recipe_view.templ
··· 1 1 package pages 2 2 3 3 import ( 4 + "fmt" 4 5 "tangled.org/arabica.social/arabica/internal/firehose" 5 6 "tangled.org/arabica.social/arabica/internal/models" 6 7 "tangled.org/arabica.social/arabica/internal/web/bff" 7 8 "tangled.org/arabica.social/arabica/internal/web/components" 8 - "fmt" 9 9 ) 10 10 11 11 type RecipeViewProps struct { ··· 68 68 <!-- Pours --> 69 69 if len(props.Recipe.Pours) > 0 { 70 70 <div class="section-box"> 71 - <h3 class="text-sm font-medium text-brown-600 uppercase tracking-wider mb-2"><span class="inline-flex items-center gap-1">@components.IconDroplet() 72 - Pours</span></h3> 71 + <h3 class="text-sm font-medium text-brown-600 uppercase tracking-wider mb-2"> 72 + <span class="inline-flex items-center gap-1"> 73 + @components.IconDroplet() 74 + Pours 75 + </span> 76 + </h3> 73 77 <div class="space-y-2"> 74 78 for _, pour := range props.Recipe.Pours { 75 79 <div class="flex justify-between items-center bg-white p-3 rounded-lg border border-brown-200"> ··· 85 89 <!-- Notes --> 86 90 if props.Recipe.Notes != "" { 87 91 <div class="section-box"> 88 - <h3 class="text-sm font-medium text-brown-600 uppercase tracking-wider mb-2"><span class="inline-flex items-center gap-1">@components.IconFileText() 89 - Notes</span></h3> 92 + <h3 class="text-sm font-medium text-brown-600 uppercase tracking-wider mb-2"> 93 + <span class="inline-flex items-center gap-1"> 94 + @components.IconFileText() 95 + Notes 96 + </span> 97 + </h3> 90 98 <div class="text-brown-900 whitespace-pre-wrap">{ props.Recipe.Notes }</div> 91 99 </div> 92 100 }
+6 -2
internal/web/pages/roaster_view.templ
··· 46 46 @components.DetailField(components.DetailFieldProps{Icon: components.IconMapPin(), Label: "Location", Value: props.Roaster.Location}) 47 47 if props.Roaster.Website != "" { 48 48 <div class="section-box"> 49 - <h3 class="text-sm font-medium text-brown-600 uppercase tracking-wider mb-2"><span class="inline-flex items-center gap-1">@components.IconLink() 50 - Website</span></h3> 49 + <h3 class="text-sm font-medium text-brown-600 uppercase tracking-wider mb-2"> 50 + <span class="inline-flex items-center gap-1"> 51 + @components.IconLink() 52 + Website 53 + </span> 54 + </h3> 51 55 if safeWebsite := bff.SafeWebsiteURL(props.Roaster.Website); safeWebsite != "" { 52 56 <a href={ templ.SafeURL(safeWebsite) } target="_blank" rel="noopener noreferrer" class="font-semibold text-brown-900 hover:underline"> 53 57 { safeWebsite }
+1 -1
tests/integration/harness.go
··· 25 25 26 26 "github.com/bluesky-social/indigo/atproto/atclient" 27 27 "github.com/bluesky-social/indigo/atproto/syntax" 28 - "tangled.org/pdewey.com/atp/testpds" 29 28 "github.com/rs/zerolog" 30 29 zlog "github.com/rs/zerolog/log" 31 30 "github.com/stretchr/testify/require" 32 31 gormlogger "gorm.io/gorm/logger" 33 32 "tangled.org/pdewey.com/atp" 33 + "tangled.org/pdewey.com/atp/testpds" 34 34 ) 35 35 36 36 func init() {
+1 -1
tests/integration/pds_test.go
··· 13 13 14 14 "github.com/bluesky-social/indigo/atproto/atclient" 15 15 "github.com/bluesky-social/indigo/atproto/syntax" 16 - "tangled.org/pdewey.com/atp/testpds" 17 16 "github.com/stretchr/testify/assert" 18 17 "github.com/stretchr/testify/require" 19 18 "tangled.org/pdewey.com/atp" 19 + "tangled.org/pdewey.com/atp/testpds" 20 20 ) 21 21 22 22 // testAccount holds credentials for a test PDS account.