diff --git a/appview/db/db.go b/appview/db/db.go index f4d5d93a..2d45e026 100644 --- a/appview/db/db.go +++ b/appview/db/db.go @@ -1078,7 +1078,7 @@ func Make(ctx context.Context, dbPath string) (*DB, error) { // transfer data, constructing pull_at from pulls table _, err = tx.Exec(` insert into pull_submissions_new (id, pull_at, round_number, patch, created) - select + select ps.id, 'at://' || p.owner_did || '/sh.tangled.repo.pull/' || p.rkey, ps.round_number, @@ -1173,6 +1173,13 @@ func Make(ctx context.Context, dbPath string) (*DB, error) { return err }) + orm.RunMigration(conn, logger, "add-punchcard-setting-profile", func(tx *sql.Tx) error { + _, err := tx.Exec(` + alter table profile add column punchard_setting string; + `) + return err + }) + return &DB{ db, logger, diff --git a/appview/db/profile.go b/appview/db/profile.go index 37537e27..511300e4 100644 --- a/appview/db/profile.go +++ b/appview/db/profile.go @@ -16,7 +16,7 @@ import ( const TimeframeMonths = 7 -func MakeProfileTimeline(e Execer, forDid string) (*models.ProfileTimeline, error) { +func MakeProfileTimeline(e Execer, forDid string, includePunchcard bool) (*models.ProfileTimeline, error) { timeline := models.ProfileTimeline{ ByMonth: make([]models.ByMonth, TimeframeMonths), } @@ -98,27 +98,29 @@ func MakeProfileTimeline(e Execer, forDid string) (*models.ProfileTimeline, erro }) } - punchcard, err := MakePunchcard( - e, - orm.FilterEq("did", forDid), - orm.FilterGte("date", time.Now().AddDate(0, -TimeframeMonths, 0)), - ) - if err != nil { - return nil, fmt.Errorf("error getting commits by did: %w", err) - } - for _, punch := range punchcard.Punches { - if punch.Date.After(now) { - continue + if includePunchcard { + punchcard, err := MakePunchcard( + e, + orm.FilterEq("did", forDid), + orm.FilterGte("date", time.Now().AddDate(0, -TimeframeMonths, 0)), + ) + if err != nil { + return nil, fmt.Errorf("error getting commits by did: %w", err) } + for _, punch := range punchcard.Punches { + if punch.Date.After(now) { + continue + } - monthsAgo := monthsBetween(punch.Date, now) - if monthsAgo >= TimeframeMonths { - // shouldn't happen; but times are weird - continue - } + monthsAgo := monthsBetween(punch.Date, now) + if monthsAgo >= TimeframeMonths { + // shouldn't happen; but times are weird + continue + } - idx := monthsAgo - timeline.ByMonth[idx].Commits += punch.Count + idx := monthsAgo + timeline.ByMonth[idx].Commits += punch.Count + } } return &timeline, nil @@ -353,9 +355,9 @@ func GetProfile(e Execer, did string) (*models.Profile, error) { includeBluesky := 0 err := e.QueryRow( - `select description, include_bluesky, location, pronouns from profile where did = ?`, + `select description, include_bluesky, location, pronouns, punchard_setting from profile where did = ?`, did, - ).Scan(&profile.Description, &includeBluesky, &profile.Location, &pronouns) + ).Scan(&profile.Description, &includeBluesky, &profile.Location, &pronouns, &profile.PunchardSetting) if err == sql.ErrNoRows { profile := models.Profile{} profile.Did = did @@ -536,3 +538,11 @@ func validateLinks(profile *models.Profile) error { } return nil } + +func SetProfilePunchcardStatus(e Execer, did string, punchcard models.ProfilePunchcardOption) error { + _, err := e.Exec( + `update profile set punchard_setting = ? where did = ?`, + punchcard, did, + ) + return err +} diff --git a/appview/models/profile.go b/appview/models/profile.go index 193ce448..b4eeb58d 100644 --- a/appview/models/profile.go +++ b/appview/models/profile.go @@ -7,19 +7,38 @@ import ( "tangled.org/core/api/tangled" ) +type ProfilePunchcardOption string + +const ( + ProfilePunchcardOptionHideMine ProfilePunchcardOption = "HIDE_MINE" + ProfilePunchcardOptionHideAll ProfilePunchcardOption = "HIDE_ALL" +) + +func ProfilePunchcardFromString(s string) ProfilePunchcardOption { + switch s { + case "HIDE_MINE": + return ProfilePunchcardOptionHideMine + case "HIDE_ALL": + return ProfilePunchcardOptionHideAll + default: + return "" + } +} + type Profile struct { // ids ID int Did string // data - Description string - IncludeBluesky bool - Location string - Links [5]string - Stats [2]VanityStat - PinnedRepos [6]syntax.ATURI - Pronouns string + Description string + IncludeBluesky bool + Location string + Links [5]string + Stats [2]VanityStat + PinnedRepos [6]syntax.ATURI + Pronouns string + PunchardSetting ProfilePunchcardOption } func (p Profile) IsLinksEmpty() bool { diff --git a/appview/pages/pages.go b/appview/pages/pages.go index aa579bc5..319d23c5 100644 --- a/appview/pages/pages.go +++ b/appview/pages/pages.go @@ -337,9 +337,10 @@ func (p *Pages) GoodFirstIssues(w io.Writer, params GoodFirstIssuesParams) error } type UserProfileSettingsParams struct { - LoggedInUser *oauth.MultiAccountUser - Tabs []map[string]any - Tab string + LoggedInUser *oauth.MultiAccountUser + Tabs []map[string]any + Tab string + PunchcardSetting models.ProfilePunchcardOption } func (p *Pages) UserProfileSettings(w io.Writer, params UserProfileSettingsParams) error { @@ -537,6 +538,7 @@ type ProfileOverviewParams struct { ProfileTimeline *models.ProfileTimeline Card *ProfileCard Active string + ShowPunchcard bool } func (p *Pages) ProfileOverview(w io.Writer, params ProfileOverviewParams) error { diff --git a/appview/pages/templates/layouts/profilebase.html b/appview/pages/templates/layouts/profilebase.html index 0747a39f..2ca283cd 100644 --- a/appview/pages/templates/layouts/profilebase.html +++ b/appview/pages/templates/layouts/profilebase.html @@ -10,7 +10,7 @@ - + @@ -28,7 +28,9 @@
{{ template "user/fragments/profileCard" .Card }} - {{ block "punchcard" .Card.Punchcard }} {{ end }} + {{ if .ShowPunchcard }} + {{ block "punchcard" .Card.Punchcard }} {{ end }} + {{ end }}
diff --git a/appview/pages/templates/user/settings/profile.html b/appview/pages/templates/user/settings/profile.html index 5c9a874e..3c3d76e7 100644 --- a/appview/pages/templates/user/settings/profile.html +++ b/appview/pages/templates/user/settings/profile.html @@ -59,4 +59,35 @@ +
+
+
+
+ Punchcard settings +
+
+ in all scenarios unless it is preceded by a disabled select?? No idea, could just be a Firefox thing? */}} + + + + + +
+
+
+
{{ end }} diff --git a/appview/settings/settings.go b/appview/settings/settings.go index 2a337e8b..e3845408 100644 --- a/appview/settings/settings.go +++ b/appview/settings/settings.go @@ -82,12 +82,21 @@ func (s *Settings) Router() http.Handler { func (s *Settings) profileSettings(w http.ResponseWriter, r *http.Request) { user := s.OAuth.GetMultiAccountUser(r) + profile, err := db.GetProfile(s.Db, user.Did()) + if err != nil { + log.Printf("failed to get profile to check punchcard settings: %s", err) + } - s.Pages.UserProfileSettings(w, pages.UserProfileSettingsParams{ + params := pages.UserProfileSettingsParams{ LoggedInUser: user, Tabs: settingsTabs, Tab: "profile", - }) + } + if profile != nil { + params.PunchcardSetting = profile.PunchardSetting + } + + s.Pages.UserProfileSettings(w, params) } func (s *Settings) notificationsSettings(w http.ResponseWriter, r *http.Request) { diff --git a/appview/state/profile.go b/appview/state/profile.go index c60af271..95d874b4 100644 --- a/appview/state/profile.go +++ b/appview/state/profile.go @@ -157,17 +157,46 @@ func (s *State) profileOverview(w http.ResponseWriter, r *http.Request) { } } - timeline, err := db.MakeProfileTimeline(s.db, profile.UserDid) + loggedInUser := s.oauth.GetMultiAccountUser(r) + + forProfile, err := db.GetProfile(s.db, profile.UserDid) + if err != nil { + l.Error("failed to get for profile to check punchcard settings", "err", err) + } + requesterProfile, err := db.GetProfile(s.db, loggedInUser.Did()) + if err != nil { + l.Error("failed to get requester profile to check punchcard settings", "err", err) + } + + showPunchcard := true + if forProfile != nil && forProfile.PunchardSetting == models.ProfilePunchcardOptionHideMine { + showPunchcard = false + } + if requesterProfile != nil && requesterProfile.PunchardSetting == models.ProfilePunchcardOptionHideAll { + showPunchcard = false + } + + timeline, err := db.MakeProfileTimeline(s.db, profile.UserDid, showPunchcard) if err != nil { l.Error("failed to create timeline", "err", err) } + loggedInUserProfile, err := db.GetProfile(s.db, loggedInUser.Did()) + if err != nil { + l.Error("failed to get logged in user profile to check punchcard settings", "err", err) + } + + if loggedInUserProfile != nil && loggedInUserProfile.PunchardSetting == models.ProfilePunchcardOptionHideAll { + + } + s.pages.ProfileOverview(w, pages.ProfileOverviewParams{ - LoggedInUser: s.oauth.GetMultiAccountUser(r), + LoggedInUser: loggedInUser, Card: profile, Repos: pinnedRepos, CollaboratingRepos: pinnedCollaboratingRepos, ProfileTimeline: timeline, + ShowPunchcard: showPunchcard, }) } @@ -404,7 +433,7 @@ func (s *State) AtomFeedPage(w http.ResponseWriter, r *http.Request) { } func (s *State) getProfileFeed(ctx context.Context, id *identity.Identity) (*feeds.Feed, error) { - timeline, err := db.MakeProfileTimeline(s.db, id.DID.String()) + timeline, err := db.MakeProfileTimeline(s.db, id.DID.String(), false) if err != nil { return nil, err } @@ -728,3 +757,31 @@ func (s *State) EditPinsFragment(w http.ResponseWriter, r *http.Request) { AllRepos: allRepos, }) } + +func (s *State) UpdateProfilePunchcardSetting(w http.ResponseWriter, r *http.Request) { + err := r.ParseForm() + if err != nil { + log.Println("invalid profile update form", err) + return + } + user := s.oauth.GetUser(r) + + profile, err := db.GetProfile(s.db, user.Did) + if err != nil { + log.Printf("getting profile data for %s: %s", user.Did, err) + } + + if profile == nil { + return + } + + punchcard := r.Form.Get("punchcard-setting") + + err = db.SetProfilePunchcardStatus(s.db, profile.Did, models.ProfilePunchcardFromString(punchcard)) + if err != nil { + log.Println("failed to update profile", err) + return + } + + s.pages.HxRefresh(w) +} diff --git a/appview/state/router.go b/appview/state/router.go index cdbda927..50f556ff 100644 --- a/appview/state/router.go +++ b/appview/state/router.go @@ -165,6 +165,7 @@ func (s *State) StandardRouter(mw *middleware.Middleware) http.Handler { r.Get("/edit-pins", s.EditPinsFragment) r.Post("/bio", s.UpdateProfileBio) r.Post("/pins", s.UpdateProfilePins) + r.Post("/punchcard", s.UpdateProfilePunchcardSetting) }) r.Mount("/settings", s.SettingsRouter())