From 027b82dabbf32314dfc5e14c5511b68fd0598ec3 Mon Sep 17 00:00:00 2001 From: oppiliappan Date: Thu, 18 Jun 2026 12:31:37 +0000 Subject: [PATCH] appview/oauth: navigate first-time tangled users to welcome screen Signed-off-by: oppiliappan --- appview/db/profile.go | 44 ++++++++++++++++++++++++++++++++++++++++++++ appview/oauth/handler.go | 66 +++++++++++++++++++++++------------------------------------------- appview/state/profile.go | 6 ++++++ 3 file(s) changed, 73 insertion(s)(+), 43 deletion(s)(-) diff --git a/appview/db/profile.go b/appview/db/profile.go --- a/appview/db/profile.go +++ b/appview/db/profile.go @@ -398,6 +398,50 @@ return syntax.DID(did), nil } +// whether a DID has authored any Tangled record, counts some common records towards this +func IsTangledUser(e Execer, did string) (bool, error) { + profile, err := GetProfile(e, did) + if err != nil { + return false, err + } + if profile != nil { + return true, nil + } + + keys, err := GetPublicKeysForDid(e, did) + if err != nil { + return false, err + } + if len(keys) > 0 { + return true, nil + } + + counts := []func() (int64, error){ + func() (int64, error) { return CountRepos(e, orm.FilterEq("did", did)) }, + func() (int64, error) { return CountStrings(e, orm.FilterEq("did", did)) }, + func() (int64, error) { return CountStars(e, orm.FilterEq("did", did)) }, + } + for _, count := range counts { + n, err := count() + if err != nil { + return false, err + } + if n > 0 { + return true, nil + } + } + + stats, err := GetFollowerFollowingCount(e, did) + if err != nil { + return false, err + } + if stats.Following > 0 { + return true, nil + } + + return false, nil +} + func GetProfile(e Execer, did string) (*models.Profile, error) { var profile models.Profile var pronouns sql.Null[string] diff --git a/appview/oauth/handler.go b/appview/oauth/handler.go --- a/appview/oauth/handler.go +++ b/appview/oauth/handler.go @@ -15,7 +15,6 @@ comatproto "github.com/bluesky-social/indigo/api/atproto" "github.com/bluesky-social/indigo/atproto/auth/oauth" "github.com/bluesky-social/indigo/atproto/syntax" - lexutil "github.com/bluesky-social/indigo/lex/util" xrpc "github.com/bluesky-social/indigo/xrpc" "github.com/go-chi/chi/v5" "github.com/posthog/posthog-go" @@ -95,9 +94,29 @@ o.Logger.Debug("session saved successfully") + did := sessData.AccountDID.String() + + // default to true, so users don't have to onboard again + isTangledUser, err := db.IsTangledUser(o.Db, did) + if err != nil { + isTangledUser = true + } + + isNewUser := !isTangledUser + if isNewUser { + if ob, _ := db.GetOnboarding(o.Db, did); ob == nil { + if err := db.UpsertOnboarding(o.Db, &models.Onboarding{ + Did: did, + Step: models.OnboardingStepProfile, + Status: models.OnboardingInProgress, + }); err != nil { + o.Logger.Error("failed to seed onboarding record", "did", did, "err", err) + } + } + } + go o.addToDefaultKnot(sessData.AccountDID) go o.addToDefaultSpindle(sessData.AccountDID.String()) - go o.ensureTangledProfile(sessData) go o.autoClaimTnglShDomain(sessData.AccountDID.String()) if !o.Config.Core.Dev { @@ -116,6 +135,8 @@ if o.isAccountDeactivated(sessData) { redirectURL = "/settings/profile" + } else if isNewUser { + redirectURL = "/welcome" } http.Redirect(w, r, redirectURL, http.StatusFound) @@ -302,47 +323,6 @@ } return nil -} - -func (o *OAuth) ensureTangledProfile(sessData *oauth.ClientSessionData) { - ctx := context.Background() - did := sessData.AccountDID.String() - l := o.Logger.With("did", did) - - profile, _ := db.GetProfile(o.Db, did) - if profile != nil { - l.Debug("profile already exists in DB") - return - } - - l.Debug("creating empty Tangled profile") - - sess, err := o.resumeSession(ctx, sessData.AccountDID, sessData.SessionID) - if err != nil { - l.Error("failed to resume session for profile creation", "err", err) - return - } - client := sess.APIClient() - - _, err = comatproto.RepoPutRecord(ctx, client, &comatproto.RepoPutRecord_Input{ - Collection: tangled.ActorProfileNSID, - Repo: did, - Rkey: "self", - Record: &lexutil.LexiconTypeDecoder{Val: &tangled.ActorProfile{}}, - }) - - if err != nil { - l.Error("failed to create empty profile on PDS", "err", err) - return - } - - emptyProfile := &models.Profile{Did: did} - if err := db.UpsertProfile(o.Db, emptyProfile); err != nil { - l.Error("failed to create empty profile in DB", "err", err) - return - } - - l.Debug("successfully created empty Tangled profile on PDS and DB") } // create a AppPasswordSession using apppasswords diff --git a/appview/state/profile.go b/appview/state/profile.go --- a/appview/state/profile.go +++ b/appview/state/profile.go @@ -90,6 +90,11 @@ profile = &models.Profile{Did: did} } + isTangledUser, err := db.IsTangledUser(s.db, did) + if err != nil { + return nil, fmt.Errorf("failed to determine tangled user status: %w", err) + } + repoCount, err := db.CountRepos(s.db, orm.FilterEq("did", did)) if err != nil { return nil, fmt.Errorf("failed to get repo count: %w", err) @@ -141,6 +146,7 @@ return &pages.ProfileCard{ UserDid: did, HasProfile: hasProfile, + IsTangledUser: isTangledUser, Profile: profile, FollowStatus: followStatus, VouchRelationship: vouchRelationship, -- tangled.sh