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 1174 return err 1175 1175 }) 1176 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 + 1177 1184 return &DB{ 1178 1185 db, 1179 1186 logger,
+11 -4
appview/db/profile.go
··· 130 130 description, 131 131 include_bluesky, 132 132 location, 133 - pronouns 133 + pronouns, 134 + default_knot 134 135 ) 135 - values (?, ?, ?, ?, ?)`, 136 + values (?, ?, ?, ?, ?, ?)`, 136 137 profile.Did, 137 138 profile.Description, 138 139 includeBskyValue, 139 140 profile.Location, 140 141 profile.Pronouns, 142 + profile.DefaultKnot, 141 143 ) 142 144 143 145 if err != nil { ··· 311 313 func GetProfile(e Execer, did string) (*models.Profile, error) { 312 314 var profile models.Profile 313 315 var pronouns sql.Null[string] 316 + var defaultKnot sql.Null[string] 314 317 315 318 profile.Did = did 316 319 317 320 includeBluesky := 0 318 321 319 322 err := e.QueryRow( 320 - `select description, include_bluesky, location, pronouns from profile where did = ?`, 323 + `select description, include_bluesky, location, pronouns, default_knot from profile where did = ?`, 321 324 did, 322 - ).Scan(&profile.Description, &includeBluesky, &profile.Location, &pronouns) 325 + ).Scan(&profile.Description, &includeBluesky, &profile.Location, &pronouns, &defaultKnot) 323 326 if err == sql.ErrNoRows { 324 327 profile := models.Profile{} 325 328 profile.Did = did ··· 336 339 337 340 if pronouns.Valid { 338 341 profile.Pronouns = pronouns.V 342 + } 343 + 344 + if defaultKnot.Valid { 345 + profile.DefaultKnot = defaultKnot.V 339 346 } 340 347 341 348 rows, err := e.Query(`select link from profile_links where did = ?`, did)
+10
appview/knots/knots.go
··· 80 80 return 81 81 } 82 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 + 83 92 k.Pages.Knots(w, pages.KnotsParams{ 84 93 LoggedInUser: user, 85 94 Registrations: registrations, 86 95 Tabs: knotsTabs, 87 96 Tab: "knots", 97 + DefaultKnot: defaultKnot, 88 98 }) 89 99 } 90 100
+1
appview/models/profile.go
··· 20 20 Stats [2]VanityStat 21 21 PinnedRepos [6]syntax.ATURI 22 22 Pronouns string 23 + DefaultKnot string 23 24 } 24 25 25 26 func (p Profile) IsLinksEmpty() bool {
+3
appview/pages/pages.go
··· 409 409 Registrations []models.Registration 410 410 Tabs []map[string]any 411 411 Tab string 412 + DefaultKnot string 412 413 } 413 414 414 415 func (p *Pages) Knots(w io.Writer, params KnotsParams) error { ··· 474 475 type NewRepoParams struct { 475 476 LoggedInUser *oauth.User 476 477 Knots []string 478 + DefaultKnot string 477 479 } 478 480 479 481 func (p *Pages) NewRepo(w io.Writer, params NewRepoParams) error { ··· 484 486 LoggedInUser *oauth.User 485 487 Knots []string 486 488 RepoInfo repoinfo.RepoInfo 489 + DefaultKnot string 487 490 } 488 491 489 492 func (p *Pages) ForkRepo(w io.Writer, params ForkRepoParams) error {
+24
appview/pages/templates/knots/index.html
··· 31 31 <div class="flex flex-col gap-6"> 32 32 {{ block "list" . }} {{ end }} 33 33 {{ block "register" . }} {{ end }} 34 + {{ block "default-knot" . }} {{ end }} 34 35 </div> 35 36 </section> 36 37 {{ end }} ··· 59 60 {{ end }} 60 61 </div> 61 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> 62 86 </section> 63 87 {{ end }} 64 88
+3 -1
appview/pages/templates/repo/fork.html
··· 25 25 value="{{ . }}" 26 26 class="mr-2" 27 27 id="domain-{{ . }}" 28 - {{if eq (len $.Knots) 1}}checked{{end}} 28 + {{if eq (len $.Knots) 1}}checked 29 + {{else if eq $.DefaultKnot . }}checked 30 + {{end}} 29 31 /> 30 32 <label for="domain-{{ . }}" class="dark:text-white">{{ . }}</label> 31 33 </div>
+3 -1
appview/pages/templates/repo/new.html
··· 155 155 class="mr-2" 156 156 id="domain-{{ . }}" 157 157 required 158 - {{if eq (len $.Knots) 1}}checked{{end}} 158 + {{if eq (len $.Knots) 1}}checked 159 + {{else if eq $.DefaultKnot . }}checked 160 + {{end}} 159 161 /> 160 162 <label for="domain-{{ . }}" class="dark:text-white lowercase">{{ . }}</label> 161 163 </div>
+10
appview/repo/repo.go
··· 1003 1003 return 1004 1004 } 1005 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 + 1006 1015 rp.pages.ForkRepo(w, pages.ForkRepoParams{ 1007 1016 LoggedInUser: user, 1008 1017 Knots: knots, 1009 1018 RepoInfo: rp.repoResolver.GetRepoInfo(r, user), 1019 + DefaultKnot: defaultKnot, 1010 1020 }) 1011 1021 1012 1022 case http.MethodPost:
+32
appview/state/profile.go
··· 613 613 s.updateProfile(profile, w, r) 614 614 } 615 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 + 616 648 func (s *State) updateProfile(profile *models.Profile, w http.ResponseWriter, r *http.Request) { 617 649 user := s.oauth.GetUser(r) 618 650 tx, err := s.db.BeginTx(r.Context(), nil)
+1
appview/state/router.go
··· 162 162 r.Get("/edit-pins", s.EditPinsFragment) 163 163 r.Post("/bio", s.UpdateProfileBio) 164 164 r.Post("/pins", s.UpdateProfilePins) 165 + r.Post("/default-knot", s.UpdateProfileDefaultKnot) 165 166 }) 166 167 167 168 r.Mount("/settings", s.SettingsRouter())
+10
appview/state/state.go
··· 453 453 return 454 454 } 455 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 + 456 465 s.pages.NewRepo(w, pages.NewRepoParams{ 457 466 LoggedInUser: user, 458 467 Knots: knots, 468 + DefaultKnot: defaultKnot, 459 469 }) 460 470 461 471 case http.MethodPost: