An event companion and onboarding experience for the ATmosphere (alpha) atmo.quest
6

Configure Feed

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

feat(connections): render current-event association checkbox

Add CurrentEventURI, CurrentEventName, ConnRecordURI, and AssociatedWithCurrent
fields to ProfileView, conditionally render a "met at <event>" checkbox inside
the .pv-notes-section when both fields are non-empty, and include connection-event.js.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

authored by

Brittany Ellich
Claude Opus 4.8
and committed by
Tangled
(Jun 1, 2026, 3:21 PM UTC) 19639e01 1adb9a47

+94 -19
+18
features/connections/pages/profile.templ
··· 26 26 EventName string // event where connection happened, if any 27 27 Notes string // private notes from SQLite 28 28 FollowUp bool // follow-up flag from SQLite 29 + // Current-event association (only populated for ATProto viewers who are 30 + // checked into an ongoing event AND have a PDS connection record here). 31 + CurrentEventURI string // at-uri of the ongoing event, "" if not checked in 32 + CurrentEventName string // display name for the checkbox label 33 + ConnRecordURI string // at-uri (rkey) of the record to update, "" if none 34 + AssociatedWithCurrent bool // initial checked state 29 35 } 30 36 31 37 // ProfileLink is a labeled URL from the target's quest.atmo.profile. ··· 166 172 } 167 173 <span class="pv-followup-text">⚑ follow up after the conference</span> 168 174 </label> 175 + if v.CurrentEventURI != "" && v.ConnRecordURI != "" { 176 + <label class="pv-event-toggle" data-target-did={ v.TargetDID }> 177 + if v.AssociatedWithCurrent { 178 + <input type="checkbox" id="pv-event-associate" checked/> 179 + } else { 180 + <input type="checkbox" id="pv-event-associate"/> 181 + } 182 + <span class="pv-event-text">📍 met at { v.CurrentEventName }</span> 183 + <span class="pv-event-saved muted" id="pv-event-status"></span> 184 + </label> 185 + } 169 186 </div> 170 187 </div> 171 188 ··· 183 200 </footer> 184 201 @layouts.BottomNav("connections") 185 202 <script src="/static/js/notes.js" defer></script> 203 + <script src="/static/js/connection-event.js" defer></script> 186 204 } 187 205 } 188 206
+76 -19
features/connections/pages/profile_templ.go
··· 34 34 EventName string // event where connection happened, if any 35 35 Notes string // private notes from SQLite 36 36 FollowUp bool // follow-up flag from SQLite 37 + // Current-event association (only populated for ATProto viewers who are 38 + // checked into an ongoing event AND have a PDS connection record here). 39 + CurrentEventURI string // at-uri of the ongoing event, "" if not checked in 40 + CurrentEventName string // display name for the checkbox label 41 + ConnRecordURI string // at-uri (rkey) of the record to update, "" if none 42 + AssociatedWithCurrent bool // initial checked state 37 43 } 38 44 39 45 // ProfileLink is a labeled URL from the target's quest.atmo.profile. ··· 90 96 var templ_7745c5c3_Var3 string 91 97 templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs("@") 92 98 if templ_7745c5c3_Err != nil { 93 - return templ.Error{Err: templ_7745c5c3_Err, FileName: `features/connections/pages/profile.templ`, Line: 49, Col: 56} 99 + return templ.Error{Err: templ_7745c5c3_Err, FileName: `features/connections/pages/profile.templ`, Line: 55, Col: 56} 94 100 } 95 101 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3)) 96 102 if templ_7745c5c3_Err != nil { ··· 103 109 var templ_7745c5c3_Var4 string 104 110 templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(shortDID(v.TargetDID)) 105 111 if templ_7745c5c3_Err != nil { 106 - return templ.Error{Err: templ_7745c5c3_Err, FileName: `features/connections/pages/profile.templ`, Line: 50, Col: 62} 112 + return templ.Error{Err: templ_7745c5c3_Err, FileName: `features/connections/pages/profile.templ`, Line: 56, Col: 62} 107 113 } 108 114 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4)) 109 115 if templ_7745c5c3_Err != nil { ··· 121 127 var templ_7745c5c3_Var5 string 122 128 templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.ResolveAttributeValue(v.AvatarURL) 123 129 if templ_7745c5c3_Err != nil { 124 - return templ.Error{Err: templ_7745c5c3_Err, FileName: `features/connections/pages/profile.templ`, Line: 57, Col: 48} 130 + return templ.Error{Err: templ_7745c5c3_Err, FileName: `features/connections/pages/profile.templ`, Line: 63, Col: 48} 125 131 } 126 132 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var5) 127 133 if templ_7745c5c3_Err != nil { ··· 134 140 var templ_7745c5c3_Var6 string 135 141 templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.ResolveAttributeValue(avatarAlt(v.DisplayName)) 136 142 if templ_7745c5c3_Err != nil { 137 - return templ.Error{Err: templ_7745c5c3_Err, FileName: `features/connections/pages/profile.templ`, Line: 57, Col: 81} 143 + return templ.Error{Err: templ_7745c5c3_Err, FileName: `features/connections/pages/profile.templ`, Line: 63, Col: 81} 138 144 } 139 145 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var6) 140 146 if templ_7745c5c3_Err != nil { ··· 162 168 var templ_7745c5c3_Var7 string 163 169 templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(v.DisplayName) 164 170 if templ_7745c5c3_Err != nil { 165 - return templ.Error{Err: templ_7745c5c3_Err, FileName: `features/connections/pages/profile.templ`, Line: 64, Col: 43} 171 + return templ.Error{Err: templ_7745c5c3_Err, FileName: `features/connections/pages/profile.templ`, Line: 70, Col: 43} 166 172 } 167 173 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7)) 168 174 if templ_7745c5c3_Err != nil { ··· 186 192 var templ_7745c5c3_Var8 string 187 193 templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs("@" + v.Handle) 188 194 if templ_7745c5c3_Err != nil { 189 - return templ.Error{Err: templ_7745c5c3_Err, FileName: `features/connections/pages/profile.templ`, Line: 69, Col: 62} 195 + return templ.Error{Err: templ_7745c5c3_Err, FileName: `features/connections/pages/profile.templ`, Line: 75, Col: 62} 190 196 } 191 197 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8)) 192 198 if templ_7745c5c3_Err != nil { ··· 204 210 var templ_7745c5c3_Var9 string 205 211 templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(v.TargetDID) 206 212 if templ_7745c5c3_Err != nil { 207 - return templ.Error{Err: templ_7745c5c3_Err, FileName: `features/connections/pages/profile.templ`, Line: 71, Col: 59} 213 + return templ.Error{Err: templ_7745c5c3_Err, FileName: `features/connections/pages/profile.templ`, Line: 77, Col: 59} 208 214 } 209 215 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9)) 210 216 if templ_7745c5c3_Err != nil { ··· 227 233 var templ_7745c5c3_Var10 string 228 234 templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.JoinStringErrs(v.ConnectedAt) 229 235 if templ_7745c5c3_Err != nil { 230 - return templ.Error{Err: templ_7745c5c3_Err, FileName: `features/connections/pages/profile.templ`, Line: 80, Col: 75} 236 + return templ.Error{Err: templ_7745c5c3_Err, FileName: `features/connections/pages/profile.templ`, Line: 86, Col: 75} 231 237 } 232 238 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var10)) 233 239 if templ_7745c5c3_Err != nil { ··· 246 252 var templ_7745c5c3_Var11 string 247 253 templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinStringErrs(v.EventName) 248 254 if templ_7745c5c3_Err != nil { 249 - return templ.Error{Err: templ_7745c5c3_Err, FileName: `features/connections/pages/profile.templ`, Line: 84, Col: 58} 255 + return templ.Error{Err: templ_7745c5c3_Err, FileName: `features/connections/pages/profile.templ`, Line: 90, Col: 58} 250 256 } 251 257 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11)) 252 258 if templ_7745c5c3_Err != nil { ··· 304 310 var templ_7745c5c3_Var12 string 305 311 templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinStringErrs(v.WorksAt) 306 312 if templ_7745c5c3_Err != nil { 307 - return templ.Error{Err: templ_7745c5c3_Err, FileName: `features/connections/pages/profile.templ`, Line: 104, Col: 49} 313 + return templ.Error{Err: templ_7745c5c3_Err, FileName: `features/connections/pages/profile.templ`, Line: 110, Col: 49} 308 314 } 309 315 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12)) 310 316 if templ_7745c5c3_Err != nil { ··· 323 329 var templ_7745c5c3_Var13 string 324 330 templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.JoinStringErrs(v.Location) 325 331 if templ_7745c5c3_Err != nil { 326 - return templ.Error{Err: templ_7745c5c3_Err, FileName: `features/connections/pages/profile.templ`, Line: 110, Col: 50} 332 + return templ.Error{Err: templ_7745c5c3_Err, FileName: `features/connections/pages/profile.templ`, Line: 116, Col: 50} 327 333 } 328 334 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var13)) 329 335 if templ_7745c5c3_Err != nil { ··· 342 348 var templ_7745c5c3_Var14 string 343 349 templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinStringErrs(v.ContactMethod) 344 350 if templ_7745c5c3_Err != nil { 345 - return templ.Error{Err: templ_7745c5c3_Err, FileName: `features/connections/pages/profile.templ`, Line: 116, Col: 55} 351 + return templ.Error{Err: templ_7745c5c3_Err, FileName: `features/connections/pages/profile.templ`, Line: 122, Col: 55} 346 352 } 347 353 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var14)) 348 354 if templ_7745c5c3_Err != nil { ··· 371 377 var templ_7745c5c3_Var15 templ.SafeURL 372 378 templ_7745c5c3_Var15, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(link.URL)) 373 379 if templ_7745c5c3_Err != nil { 374 - return templ.Error{Err: templ_7745c5c3_Err, FileName: `features/connections/pages/profile.templ`, Line: 129, Col: 42} 380 + return templ.Error{Err: templ_7745c5c3_Err, FileName: `features/connections/pages/profile.templ`, Line: 135, Col: 42} 375 381 } 376 382 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var15)) 377 383 if templ_7745c5c3_Err != nil { ··· 384 390 var templ_7745c5c3_Var16 string 385 391 templ_7745c5c3_Var16, templ_7745c5c3_Err = templ.JoinStringErrs(link.Label) 386 392 if templ_7745c5c3_Err != nil { 387 - return templ.Error{Err: templ_7745c5c3_Err, FileName: `features/connections/pages/profile.templ`, Line: 129, Col: 128} 393 + return templ.Error{Err: templ_7745c5c3_Err, FileName: `features/connections/pages/profile.templ`, Line: 135, Col: 128} 388 394 } 389 395 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var16)) 390 396 if templ_7745c5c3_Err != nil { ··· 413 419 var templ_7745c5c3_Var17 string 414 420 templ_7745c5c3_Var17, templ_7745c5c3_Err = templ.JoinStringErrs(interest) 415 421 if templ_7745c5c3_Err != nil { 416 - return templ.Error{Err: templ_7745c5c3_Err, FileName: `features/connections/pages/profile.templ`, Line: 141, Col: 58} 422 + return templ.Error{Err: templ_7745c5c3_Err, FileName: `features/connections/pages/profile.templ`, Line: 147, Col: 58} 417 423 } 418 424 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var17)) 419 425 if templ_7745c5c3_Err != nil { ··· 436 442 var templ_7745c5c3_Var18 string 437 443 templ_7745c5c3_Var18, templ_7745c5c3_Err = templ.ResolveAttributeValue(v.TargetDID) 438 444 if templ_7745c5c3_Err != nil { 439 - return templ.Error{Err: templ_7745c5c3_Err, FileName: `features/connections/pages/profile.templ`, Line: 148, Col: 64} 445 + return templ.Error{Err: templ_7745c5c3_Err, FileName: `features/connections/pages/profile.templ`, Line: 154, Col: 64} 440 446 } 441 447 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var18) 442 448 if templ_7745c5c3_Err != nil { ··· 449 455 var templ_7745c5c3_Var19 string 450 456 templ_7745c5c3_Var19, templ_7745c5c3_Err = templ.JoinStringErrs(v.Notes) 451 457 if templ_7745c5c3_Err != nil { 452 - return templ.Error{Err: templ_7745c5c3_Err, FileName: `features/connections/pages/profile.templ`, Line: 160, Col: 16} 458 + return templ.Error{Err: templ_7745c5c3_Err, FileName: `features/connections/pages/profile.templ`, Line: 166, Col: 16} 453 459 } 454 460 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var19)) 455 461 if templ_7745c5c3_Err != nil { ··· 470 476 return templ_7745c5c3_Err 471 477 } 472 478 } 473 - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 50, "<span class=\"pv-followup-text\">⚑ follow up after the conference</span></label></div></div><div class=\"pv-nav\"><a href=\"/connections\" class=\"btn btn-ghost btn-sm\">← all connections</a></div></div><div class=\"statusbar\"><a href=\"/\" class=\"statusbar-brand\">atmo.quest</a></div></div><footer><a href=\"https://tangled.org/vvill.dev/atmoquest\" rel=\"noopener noreferrer\" target=\"_blank\">source on tangled</a></footer>") 479 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 50, "<span class=\"pv-followup-text\">⚑ follow up after the conference</span></label> ") 480 + if templ_7745c5c3_Err != nil { 481 + return templ_7745c5c3_Err 482 + } 483 + if v.CurrentEventURI != "" && v.ConnRecordURI != "" { 484 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 51, "<label class=\"pv-event-toggle\" data-target-did=\"") 485 + if templ_7745c5c3_Err != nil { 486 + return templ_7745c5c3_Err 487 + } 488 + var templ_7745c5c3_Var20 string 489 + templ_7745c5c3_Var20, templ_7745c5c3_Err = templ.ResolveAttributeValue(v.TargetDID) 490 + if templ_7745c5c3_Err != nil { 491 + return templ.Error{Err: templ_7745c5c3_Err, FileName: `features/connections/pages/profile.templ`, Line: 176, Col: 67} 492 + } 493 + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var20) 494 + if templ_7745c5c3_Err != nil { 495 + return templ_7745c5c3_Err 496 + } 497 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 52, "\">") 498 + if templ_7745c5c3_Err != nil { 499 + return templ_7745c5c3_Err 500 + } 501 + if v.AssociatedWithCurrent { 502 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 53, "<input type=\"checkbox\" id=\"pv-event-associate\" checked> ") 503 + if templ_7745c5c3_Err != nil { 504 + return templ_7745c5c3_Err 505 + } 506 + } else { 507 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 54, "<input type=\"checkbox\" id=\"pv-event-associate\"> ") 508 + if templ_7745c5c3_Err != nil { 509 + return templ_7745c5c3_Err 510 + } 511 + } 512 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 55, "<span class=\"pv-event-text\">📍 met at ") 513 + if templ_7745c5c3_Err != nil { 514 + return templ_7745c5c3_Err 515 + } 516 + var templ_7745c5c3_Var21 string 517 + templ_7745c5c3_Var21, templ_7745c5c3_Err = templ.JoinStringErrs(v.CurrentEventName) 518 + if templ_7745c5c3_Err != nil { 519 + return templ.Error{Err: templ_7745c5c3_Err, FileName: `features/connections/pages/profile.templ`, Line: 182, Col: 68} 520 + } 521 + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var21)) 522 + if templ_7745c5c3_Err != nil { 523 + return templ_7745c5c3_Err 524 + } 525 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 56, "</span> <span class=\"pv-event-saved muted\" id=\"pv-event-status\"></span></label>") 526 + if templ_7745c5c3_Err != nil { 527 + return templ_7745c5c3_Err 528 + } 529 + } 530 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 57, "</div></div><div class=\"pv-nav\"><a href=\"/connections\" class=\"btn btn-ghost btn-sm\">← all connections</a></div></div><div class=\"statusbar\"><a href=\"/\" class=\"statusbar-brand\">atmo.quest</a></div></div><footer><a href=\"https://tangled.org/vvill.dev/atmoquest\" rel=\"noopener noreferrer\" target=\"_blank\">source on tangled</a></footer>") 474 531 if templ_7745c5c3_Err != nil { 475 532 return templ_7745c5c3_Err 476 533 } ··· 478 535 if templ_7745c5c3_Err != nil { 479 536 return templ_7745c5c3_Err 480 537 } 481 - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 51, " <script src=\"/static/js/notes.js\" defer></script>") 538 + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 58, " <script src=\"/static/js/notes.js\" defer></script> <script src=\"/static/js/connection-event.js\" defer></script>") 482 539 if templ_7745c5c3_Err != nil { 483 540 return templ_7745c5c3_Err 484 541 }