forked from tangled.org/core
Monorepo for Tangled — https://tangled.org

appview: allows a default knot to be configured

Signed-off-by: Will Andrews <did:plc:dadhhalkfcq3gucaq25hjqon>

Changed files
+115 -6
appview
+7
appview/db/db.go
··· 1174 return err 1175 }) 1176 1177 return &DB{ 1178 db, 1179 logger,
··· 1174 return err 1175 }) 1176 1177 + runMigration(conn, logger, "add-default-knot-profile", func(tx *sql.Tx) error { 1178 + _, err := tx.Exec(` 1179 + alter table profile add column default_knot text; 1180 + `) 1181 + return err 1182 + }) 1183 + 1184 return &DB{ 1185 db, 1186 logger,
+11 -4
appview/db/profile.go
··· 130 description, 131 include_bluesky, 132 location, 133 - pronouns 134 ) 135 - values (?, ?, ?, ?, ?)`, 136 profile.Did, 137 profile.Description, 138 includeBskyValue, 139 profile.Location, 140 profile.Pronouns, 141 ) 142 143 if err != nil { ··· 311 func GetProfile(e Execer, did string) (*models.Profile, error) { 312 var profile models.Profile 313 var pronouns sql.Null[string] 314 315 profile.Did = did 316 317 includeBluesky := 0 318 319 err := e.QueryRow( 320 - `select description, include_bluesky, location, pronouns from profile where did = ?`, 321 did, 322 - ).Scan(&profile.Description, &includeBluesky, &profile.Location, &pronouns) 323 if err == sql.ErrNoRows { 324 profile := models.Profile{} 325 profile.Did = did ··· 336 337 if pronouns.Valid { 338 profile.Pronouns = pronouns.V 339 } 340 341 rows, err := e.Query(`select link from profile_links where did = ?`, did)
··· 130 description, 131 include_bluesky, 132 location, 133 + pronouns, 134 + default_knot 135 ) 136 + values (?, ?, ?, ?, ?, ?)`, 137 profile.Did, 138 profile.Description, 139 includeBskyValue, 140 profile.Location, 141 profile.Pronouns, 142 + profile.DefaultKnot, 143 ) 144 145 if err != nil { ··· 313 func GetProfile(e Execer, did string) (*models.Profile, error) { 314 var profile models.Profile 315 var pronouns sql.Null[string] 316 + var defaultKnot sql.Null[string] 317 318 profile.Did = did 319 320 includeBluesky := 0 321 322 err := e.QueryRow( 323 + `select description, include_bluesky, location, pronouns, default_knot from profile where did = ?`, 324 did, 325 + ).Scan(&profile.Description, &includeBluesky, &profile.Location, &pronouns, &defaultKnot) 326 if err == sql.ErrNoRows { 327 profile := models.Profile{} 328 profile.Did = did ··· 339 340 if pronouns.Valid { 341 profile.Pronouns = pronouns.V 342 + } 343 + 344 + if defaultKnot.Valid { 345 + profile.DefaultKnot = defaultKnot.V 346 } 347 348 rows, err := e.Query(`select link from profile_links where did = ?`, did)
+10
appview/knots/knots.go
··· 80 return 81 } 82 83 k.Pages.Knots(w, pages.KnotsParams{ 84 LoggedInUser: user, 85 Registrations: registrations, 86 Tabs: knotsTabs, 87 Tab: "knots", 88 }) 89 } 90
··· 80 return 81 } 82 83 + defaultKnot := "" 84 + profile, err := db.GetProfile(k.Db, user.Did) 85 + if err != nil { 86 + k.Logger.Warn("gettings user profile to get default knot", "error", err) 87 + } 88 + if profile != nil { 89 + defaultKnot = profile.DefaultKnot 90 + } 91 + 92 k.Pages.Knots(w, pages.KnotsParams{ 93 LoggedInUser: user, 94 Registrations: registrations, 95 Tabs: knotsTabs, 96 Tab: "knots", 97 + DefaultKnot: defaultKnot, 98 }) 99 } 100
+1
appview/models/profile.go
··· 20 Stats [2]VanityStat 21 PinnedRepos [6]syntax.ATURI 22 Pronouns string 23 } 24 25 func (p Profile) IsLinksEmpty() bool {
··· 20 Stats [2]VanityStat 21 PinnedRepos [6]syntax.ATURI 22 Pronouns string 23 + DefaultKnot string 24 } 25 26 func (p Profile) IsLinksEmpty() bool {
+3
appview/pages/pages.go
··· 409 Registrations []models.Registration 410 Tabs []map[string]any 411 Tab string 412 } 413 414 func (p *Pages) Knots(w io.Writer, params KnotsParams) error { ··· 474 type NewRepoParams struct { 475 LoggedInUser *oauth.User 476 Knots []string 477 } 478 479 func (p *Pages) NewRepo(w io.Writer, params NewRepoParams) error { ··· 484 LoggedInUser *oauth.User 485 Knots []string 486 RepoInfo repoinfo.RepoInfo 487 } 488 489 func (p *Pages) ForkRepo(w io.Writer, params ForkRepoParams) error {
··· 409 Registrations []models.Registration 410 Tabs []map[string]any 411 Tab string 412 + DefaultKnot string 413 } 414 415 func (p *Pages) Knots(w io.Writer, params KnotsParams) error { ··· 475 type NewRepoParams struct { 476 LoggedInUser *oauth.User 477 Knots []string 478 + DefaultKnot string 479 } 480 481 func (p *Pages) NewRepo(w io.Writer, params NewRepoParams) error { ··· 486 LoggedInUser *oauth.User 487 Knots []string 488 RepoInfo repoinfo.RepoInfo 489 + DefaultKnot string 490 } 491 492 func (p *Pages) ForkRepo(w io.Writer, params ForkRepoParams) error {
+24
appview/pages/templates/knots/index.html
··· 31 <div class="flex flex-col gap-6"> 32 {{ block "list" . }} {{ end }} 33 {{ block "register" . }} {{ end }} 34 </div> 35 </section> 36 {{ end }} ··· 59 {{ end }} 60 </div> 61 <div id="operation-error" class="text-red-500 dark:text-red-400"></div> 62 </section> 63 {{ end }} 64
··· 31 <div class="flex flex-col gap-6"> 32 {{ block "list" . }} {{ end }} 33 {{ block "register" . }} {{ end }} 34 + {{ block "default-knot" . }} {{ end }} 35 </div> 36 </section> 37 {{ end }} ··· 60 {{ end }} 61 </div> 62 <div id="operation-error" class="text-red-500 dark:text-red-400"></div> 63 + </section> 64 + {{ end }} 65 + 66 + {{ define "default-knot" }} 67 + <section class="rounded w-full flex flex-col gap-2"> 68 + <h2 class="text-sm font-bold py-2 uppercase dark:text-gray-300">default knot</h2> 69 + <select id="default-knot" name="default-knot" 70 + class="p-1 max-w-64 border border-gray-200 bg-white dark:bg-gray-800 dark:text-white dark:border-gray-700" 71 + hx-post="/profile/default-knot" 72 + hx-swap="none" 73 + name="default-knot"> 74 + <option value="" > 75 + Choose a default Knot 76 + </option> 77 + <option value="knot1.tangled.sh" {{if eq $.DefaultKnot "knot1.tangled.sh"}}selected{{end}} > 78 + knot1.tangled.sh 79 + </option> 80 + {{ range $registration := .Registrations }} 81 + <option value="{{ .Domain }}" class="py-1" {{if eq $.DefaultKnot .Domain}}selected{{end}}> 82 + {{ .Domain }} 83 + </option> 84 + {{ end }} 85 + </select> 86 </section> 87 {{ end }} 88
+3 -1
appview/pages/templates/repo/fork.html
··· 25 value="{{ . }}" 26 class="mr-2" 27 id="domain-{{ . }}" 28 - {{if eq (len $.Knots) 1}}checked{{end}} 29 /> 30 <label for="domain-{{ . }}" class="dark:text-white">{{ . }}</label> 31 </div>
··· 25 value="{{ . }}" 26 class="mr-2" 27 id="domain-{{ . }}" 28 + {{if eq (len $.Knots) 1}}checked 29 + {{else if eq $.DefaultKnot . }}checked 30 + {{end}} 31 /> 32 <label for="domain-{{ . }}" class="dark:text-white">{{ . }}</label> 33 </div>
+3 -1
appview/pages/templates/repo/new.html
··· 155 class="mr-2" 156 id="domain-{{ . }}" 157 required 158 - {{if eq (len $.Knots) 1}}checked{{end}} 159 /> 160 <label for="domain-{{ . }}" class="dark:text-white lowercase">{{ . }}</label> 161 </div>
··· 155 class="mr-2" 156 id="domain-{{ . }}" 157 required 158 + {{if eq (len $.Knots) 1}}checked 159 + {{else if eq $.DefaultKnot . }}checked 160 + {{end}} 161 /> 162 <label for="domain-{{ . }}" class="dark:text-white lowercase">{{ . }}</label> 163 </div>
+10
appview/repo/repo.go
··· 1003 return 1004 } 1005 1006 rp.pages.ForkRepo(w, pages.ForkRepoParams{ 1007 LoggedInUser: user, 1008 Knots: knots, 1009 RepoInfo: rp.repoResolver.GetRepoInfo(r, user), 1010 }) 1011 1012 case http.MethodPost:
··· 1003 return 1004 } 1005 1006 + defaultKnot := "" 1007 + profile, err := db.GetProfile(rp.db, user.Did) 1008 + if err != nil { 1009 + rp.logger.Warn("gettings user profile to get default knot", "error", err) 1010 + } 1011 + if profile != nil { 1012 + defaultKnot = profile.DefaultKnot 1013 + } 1014 + 1015 rp.pages.ForkRepo(w, pages.ForkRepoParams{ 1016 LoggedInUser: user, 1017 Knots: knots, 1018 RepoInfo: rp.repoResolver.GetRepoInfo(r, user), 1019 + DefaultKnot: defaultKnot, 1020 }) 1021 1022 case http.MethodPost:
+32
appview/state/profile.go
··· 613 s.updateProfile(profile, w, r) 614 } 615 616 func (s *State) updateProfile(profile *models.Profile, w http.ResponseWriter, r *http.Request) { 617 user := s.oauth.GetUser(r) 618 tx, err := s.db.BeginTx(r.Context(), nil)
··· 613 s.updateProfile(profile, w, r) 614 } 615 616 + func (s *State) UpdateProfileDefaultKnot(w http.ResponseWriter, r *http.Request) { 617 + err := r.ParseForm() 618 + if err != nil { 619 + log.Println("invalid profile update form", err) 620 + return 621 + } 622 + user := s.oauth.GetUser(r) 623 + 624 + profile, err := db.GetProfile(s.db, user.Did) 625 + if err != nil { 626 + log.Printf("getting profile data for %s: %s", user.Did, err) 627 + } 628 + 629 + if profile == nil { 630 + return 631 + } 632 + 633 + profile.DefaultKnot = r.Form.Get("default-knot") 634 + 635 + tx, err := s.db.BeginTx(r.Context(), nil) 636 + if err != nil { 637 + log.Println("failed to start transaction", err) 638 + return 639 + } 640 + 641 + err = db.UpsertProfile(tx, profile) 642 + if err != nil { 643 + log.Println("failed to update profile", err) 644 + return 645 + } 646 + } 647 + 648 func (s *State) updateProfile(profile *models.Profile, w http.ResponseWriter, r *http.Request) { 649 user := s.oauth.GetUser(r) 650 tx, err := s.db.BeginTx(r.Context(), nil)
+1
appview/state/router.go
··· 162 r.Get("/edit-pins", s.EditPinsFragment) 163 r.Post("/bio", s.UpdateProfileBio) 164 r.Post("/pins", s.UpdateProfilePins) 165 }) 166 167 r.Mount("/settings", s.SettingsRouter())
··· 162 r.Get("/edit-pins", s.EditPinsFragment) 163 r.Post("/bio", s.UpdateProfileBio) 164 r.Post("/pins", s.UpdateProfilePins) 165 + r.Post("/default-knot", s.UpdateProfileDefaultKnot) 166 }) 167 168 r.Mount("/settings", s.SettingsRouter())
+10
appview/state/state.go
··· 453 return 454 } 455 456 s.pages.NewRepo(w, pages.NewRepoParams{ 457 LoggedInUser: user, 458 Knots: knots, 459 }) 460 461 case http.MethodPost:
··· 453 return 454 } 455 456 + defaultKnot := "" 457 + profile, err := db.GetProfile(s.db, user.Did) 458 + if err != nil { 459 + s.logger.Warn("gettings user profile to get default knot", "error", err) 460 + } 461 + if profile != nil { 462 + defaultKnot = profile.DefaultKnot 463 + } 464 + 465 s.pages.NewRepo(w, pages.NewRepoParams{ 466 LoggedInUser: user, 467 Knots: knots, 468 + DefaultKnot: defaultKnot, 469 }) 470 471 case http.MethodPost: