From adbf58908c8605c7bbb77149df0778a34ffe5edb Mon Sep 17 00:00:00 2001 From: Will Andrews Date: Wed, 25 Feb 2026 20:59:42 +0000 Subject: [PATCH] appview: allows a default knot to be configured Signed-off-by: Will Andrews --- appview/db/db.go | 8 ++++++++ appview/db/preferences.go | 35 +++++++++++++++++++++++++++++++++++ appview/knots/knots.go | 22 ++++++++++++++++++++-- appview/models/preferences.go | 6 ++++++ appview/pages/pages.go | 10 +++++++--- appview/pages/templates/knots/index.html | 28 ++++++++++++++++++++++++++++ appview/pages/templates/repo/fork.html | 4 +++- appview/pages/templates/repo/new.html | 4 +++- appview/repo/repo.go | 10 ++++++++++ appview/state/profile.go | 23 +++++++++++++++++++++++ appview/state/router.go | 1 + appview/state/state.go | 10 ++++++++++ 12 file(s) changed, 154 insertion(s)(+), 7 deletion(s)(-) diff --git a/appview/db/db.go b/appview/db/db.go --- a/appview/db/db.go +++ b/appview/db/db.go @@ -640,6 +640,14 @@ created_at text not null default (strftime('%Y-%m-%dT%H:%M:%SZ', 'now')), foreign key (repo_at) references repos(at_uri) on delete cascade ); + create table if not exists knot_preferences ( + id integer primary key autoincrement, + user_did text not null unique, + default_knot text, + + foreign key (user_did, default_knot) references registrations(did, domain) on delete cascade + ); + create table if not exists migrations ( id integer primary key autoincrement, name text unique diff --git a/appview/db/preferences.go b/appview/db/preferences.go --- a/appview/db/preferences.go +++ b/appview/db/preferences.go @@ -50,3 +50,38 @@ } return nil } + +func GetKnotPreference(e Execer, did string) (*models.KnotPreference, error) { + var knotPreference models.KnotPreference + + err := e.QueryRow( + `select id, user_did, default_knot from knot_preferences where user_did = ?`, + did, + ).Scan(&knotPreference.ID, &knotPreference.Did, &knotPreference.DefaultKnot) + if err == sql.ErrNoRows { + return nil, nil + } + + if err != nil { + return nil, err + } + + return &knotPreference, nil +} + +func UpsertKnotPreference(e Execer, did, defaultKnot string) error { + _, err := e.Exec( + `insert or replace into knot_preferences ( + user_did, + default_knot + ) + values (?, ?)`, + did, + defaultKnot, + ) + if err != nil { + return err + } + + return nil +} diff --git a/appview/knots/knots.go b/appview/knots/knots.go --- a/appview/knots/knots.go +++ b/appview/knots/knots.go @@ -68,9 +68,27 @@ w.WriteHeader(http.StatusInternalServerError) return } + availableKnots, err := k.Enforcer.GetKnotsForUser(user.Did()) + if err != nil { + k.Logger.Error("failed to fetch available knots for user", "err", err) + w.WriteHeader(http.StatusInternalServerError) + return + } + + defaultKnot := "" + knotPrefence, err := db.GetKnotPreference(k.Db, user.Did()) + if err != nil { + k.Logger.Warn("gettings users knot preferences", "error", err) + } + if knotPrefence != nil { + defaultKnot = knotPrefence.DefaultKnot + } + k.Pages.Knots(w, pages.KnotsParams{ - LoggedInUser: user, - Registrations: registrations, + LoggedInUser: user, + Registrations: registrations, + AvailableKnots: availableKnots, + DefaultKnot: defaultKnot, }) } diff --git a/appview/models/preferences.go b/appview/models/preferences.go --- a/appview/models/preferences.go +++ b/appview/models/preferences.go @@ -6,3 +6,9 @@ Did string HideMine bool HideOthers bool } + +type KnotPreference struct { + ID int + Did string + DefaultKnot string +} diff --git a/appview/pages/pages.go b/appview/pages/pages.go --- a/appview/pages/pages.go +++ b/appview/pages/pages.go @@ -524,9 +524,11 @@ return p.executePlain("banner", w, params) } type KnotsParams struct { - LoggedInUser *oauth.MultiAccountUser - Registrations []models.Registration - Tab string + LoggedInUser *oauth.MultiAccountUser + Registrations []models.Registration + Tab string + AvailableKnots []string + DefaultKnot string } func (p *Pages) Knots(w io.Writer, params KnotsParams) error { @@ -590,6 +592,7 @@ type NewRepoParams struct { LoggedInUser *oauth.MultiAccountUser Knots []string + DefaultKnot string } func (p *Pages) NewRepo(w io.Writer, params NewRepoParams) error { @@ -600,6 +603,7 @@ type ForkRepoParams struct { LoggedInUser *oauth.MultiAccountUser Knots []string RepoInfo repoinfo.RepoInfo + DefaultKnot string } func (p *Pages) ForkRepo(w io.Writer, params ForkRepoParams) error { diff --git a/appview/pages/templates/knots/index.html b/appview/pages/templates/knots/index.html --- a/appview/pages/templates/knots/index.html +++ b/appview/pages/templates/knots/index.html @@ -31,6 +31,7 @@
{{ block "list" . }} {{ end }} {{ block "register" . }} {{ end }} + {{ block "default-knot" . }} {{ end }}
{{ end }} @@ -60,6 +61,33 @@ {{ end }}
+{{ end }} + +{{ define "default-knot" }} +
+

default knot

+
+ in all scenarios unless it is preceded by a disabled select?? No idea, could just be a Firefox thing? */}} + + {{ range $.AvailableKnots }} + + {{ end }} + + +
+
{{ end }} {{ define "register" }} diff --git a/appview/pages/templates/repo/fork.html b/appview/pages/templates/repo/fork.html --- a/appview/pages/templates/repo/fork.html +++ b/appview/pages/templates/repo/fork.html @@ -25,7 +25,9 @@ name="knot" value="{{ . }}" class="mr-2" id="domain-{{ . }}" - {{if eq (len $.Knots) 1}}checked{{end}} + {{if eq (len $.Knots) 1}}checked + {{else if eq $.DefaultKnot . }}checked + {{end}} /> diff --git a/appview/pages/templates/repo/new.html b/appview/pages/templates/repo/new.html --- a/appview/pages/templates/repo/new.html +++ b/appview/pages/templates/repo/new.html @@ -156,7 +156,9 @@ value="{{ . }}" class="mr-2" id="domain-{{ . }}" required - {{if eq (len $.Knots) 1}}checked{{end}} + {{if eq (len $.Knots) 1}}checked + {{else if eq $.DefaultKnot . }}checked + {{end}} /> diff --git a/appview/repo/repo.go b/appview/repo/repo.go --- a/appview/repo/repo.go +++ b/appview/repo/repo.go @@ -1016,10 +1016,20 @@ rp.pages.Notice(w, "repo", "Invalid user account.") return } + defaultKnot := "" + knotPrefence, err := db.GetKnotPreference(rp.db, user.Did()) + if err != nil { + rp.logger.Warn("gettings users knot preferences", "error", err) + } + if knotPrefence != nil { + defaultKnot = knotPrefence.DefaultKnot + } + rp.pages.ForkRepo(w, pages.ForkRepoParams{ LoggedInUser: user, Knots: knots, RepoInfo: rp.repoResolver.GetRepoInfo(r, user), + DefaultKnot: defaultKnot, }) case http.MethodPost: diff --git a/appview/state/profile.go b/appview/state/profile.go --- a/appview/state/profile.go +++ b/appview/state/profile.go @@ -745,6 +745,29 @@ s.updateProfile(profile, w, r) } +func (s *State) UpdateDefaultKnotPreference(w http.ResponseWriter, r *http.Request) { + err := r.ParseForm() + if err != nil { + log.Println("invalid preference update form", err) + return + } + user := s.oauth.GetMultiAccountUser(r) + + defaultKnot := r.Form.Get("default-knot") + + if defaultKnot == "[[none]]" { // see pages/templates/knots/index.html for more info on why we use this value + defaultKnot = "" + } + + err = db.UpsertKnotPreference(s.db, user.Did(), defaultKnot) + if err != nil { + log.Println("failed to update default knot preference", err) + return + } + + s.pages.HxRefresh(w) +} + func (s *State) updateProfile(profile *models.Profile, w http.ResponseWriter, r *http.Request) { l := s.logger.With("handler", "updateProfile") user := s.oauth.GetMultiAccountUser(r) diff --git a/appview/state/router.go b/appview/state/router.go --- a/appview/state/router.go +++ b/appview/state/router.go @@ -198,6 +198,7 @@ r.Post("/pins", s.UpdateProfilePins) r.Post("/avatar", s.UploadProfileAvatar) r.Delete("/avatar", s.RemoveProfileAvatar) r.Post("/punchcard", s.UpdateProfilePunchcardSetting) + r.Post("/default-knot", s.UpdateDefaultKnotPreference) }) r.Mount("/settings", s.SettingsRouter()) diff --git a/appview/state/state.go b/appview/state/state.go --- a/appview/state/state.go +++ b/appview/state/state.go @@ -405,9 +405,19 @@ s.pages.Notice(w, "repo", "Invalid user account.") return } + defaultKnot := "" + knotPrefence, err := db.GetKnotPreference(s.db, user.Did()) + if err != nil { + s.logger.Warn("gettings users knot preferences", "error", err) + } + if knotPrefence != nil { + defaultKnot = knotPrefence.DefaultKnot + } + s.pages.NewRepo(w, pages.NewRepoParams{ LoggedInUser: user, Knots: knots, + DefaultKnot: defaultKnot, }) case http.MethodPost: -- tangled.sh