diff --git a/appview/ingester.go b/appview/ingester.go
index 6e5608ca..e6bdea74 100644
--- a/appview/ingester.go
+++ b/appview/ingester.go
@@ -613,7 +613,7 @@ func (i *Ingester) ingestProfile(ctx context.Context, e *jmodels.Event, l *slog.
err = db.ValidateProfile(i.Db, &profile)
if err != nil {
- return fmt.Errorf("invalid profile record")
+ return fmt.Errorf("invalid profile record: %w", err)
}
err = db.UpsertProfile(i.Db, &profile)
diff --git a/appview/pages/pages.go b/appview/pages/pages.go
index 5bae2c3b..fbfa0ac6 100644
--- a/appview/pages/pages.go
+++ b/appview/pages/pages.go
@@ -271,6 +271,15 @@ func (p *Pages) executeLogin(name string, w io.Writer, params any) error {
return err
}
+func (p *Pages) executeOnboarding(name string, w io.Writer, params any) error {
+ tpl, err := p.parseOnboardingBase(name)
+ if err != nil {
+ return err
+ }
+
+ return tpl.ExecuteTemplate(w, "layouts/base", params)
+}
+
func (p *Pages) execute(name string, w io.Writer, params any) error {
tpl, err := p.parseBase(name)
if err != nil {
@@ -705,6 +714,7 @@ func (p *Pages) ForkRepo(w io.Writer, params ForkRepoParams) error {
type ProfileCard struct {
UserDid string
HasProfile bool
+ IsTangledUser bool
FollowStatus models.FollowStatus
VouchRelationship *models.VouchRelationship
Punchcard *models.Punchcard
@@ -873,6 +883,9 @@ type EditBioParams struct {
BaseParams
Profile *models.Profile
AlsoKnownAs []string
+ // Action optionally overrides the form's hx-post target. Defaults to
+ // /profile/bio when empty. Used by the onboarding flow to save + advance.
+ Action string
}
func (p *Pages) EditBioFragment(w io.Writer, params EditBioParams) error {
@@ -907,6 +920,29 @@ func (p *Pages) StarBtnFragment(w io.Writer, params StarBtnFragmentParams) error
return p.executePlain("fragments/starBtn", w, params)
}
+type OnboardingParams struct {
+ BaseParams
+ Step int
+
+ EditBio EditBioParams
+
+ PubKeys []models.PublicKey
+
+ People []FollowCard
+ TrendingRepos []models.Repo
+ StarStatuses map[string]bool
+}
+
+// KeyFragment renders a single SSH key row (used to append a newly added key
+// to the list without a full reload).
+func (p *Pages) KeyFragment(w io.Writer, key models.PublicKey) error {
+ return p.executePlain("user/settings/fragments/keyListing", w, key)
+}
+
+func (p *Pages) Onboarding(w io.Writer, params OnboardingParams) error {
+ return p.executeOnboarding("onboarding/welcome", w, params)
+}
+
type RepoIndexParams struct {
BaseParams
RepoInfo repoinfo.RepoInfo
diff --git a/appview/pages/templates/onboarding/fragments/back.html b/appview/pages/templates/onboarding/fragments/back.html
new file mode 100644
index 00000000..e2698f7f
--- /dev/null
+++ b/appview/pages/templates/onboarding/fragments/back.html
@@ -0,0 +1,5 @@
+{{ define "onboarding/back" }}
+
+ {{ i "arrow-left" "size-4" }} Back
+
+{{ end }}
diff --git a/appview/pages/templates/onboarding/fragments/finish.html b/appview/pages/templates/onboarding/fragments/finish.html
new file mode 100644
index 00000000..b9297ea4
--- /dev/null
+++ b/appview/pages/templates/onboarding/fragments/finish.html
@@ -0,0 +1,79 @@
+{{ define "onboarding/finish" }}
+
You're all set!
+ Here are a few things to explore next:
+
+
+
+
+
+
+ {{ i "git-pull-request" "size-4" }}
+
Contribute to the Tangled project
+
+
+ Tangled is fully open-source: Open a feature request, or submit a bug-fix!
+
+
+ All contributions are welcome.
+
+
+ Contribute {{ i "arrow-right" "size-4" }}
+
+
+
+
+
+
+
+
+
+
+
+ {{ template "onboarding/back" "/welcome/keys" }}
+
+
+{{ end }}
diff --git a/appview/pages/templates/onboarding/fragments/keys.html b/appview/pages/templates/onboarding/fragments/keys.html
new file mode 100644
index 00000000..efa2e33b
--- /dev/null
+++ b/appview/pages/templates/onboarding/fragments/keys.html
@@ -0,0 +1,39 @@
+{{ define "onboarding/keys" }}
+ Add an SSH key
+ Add a public SSH key to push to your repositories over SSH.
+
+
+ {{ range .PubKeys }}
+ {{ template "user/settings/fragments/keyListing" . }}
+ {{ end }}
+
+
+
+
+
+
+
+ {{ template "onboarding/back" "/welcome/social" }}
+
+
+{{ end }}
diff --git a/appview/pages/templates/onboarding/fragments/profile.html b/appview/pages/templates/onboarding/fragments/profile.html
new file mode 100644
index 00000000..fc674e0e
--- /dev/null
+++ b/appview/pages/templates/onboarding/fragments/profile.html
@@ -0,0 +1,84 @@
+{{ define "onboarding/profile" }}
+ {{ $profile := .EditBio.Profile }}
+ Welcome!
+ Let's get your profile ready. All fields are optional.
+
+
+
+
+
+
+
+
+
+
+{{ end }}
diff --git a/appview/pages/templates/onboarding/fragments/social.html b/appview/pages/templates/onboarding/fragments/social.html
new file mode 100644
index 00000000..f4fbabbc
--- /dev/null
+++ b/appview/pages/templates/onboarding/fragments/social.html
@@ -0,0 +1,51 @@
+{{ define "onboarding/social" }}
+ Discover
+ Find users to follow and repositories to star.
+
+ {{ if .People }}
+
+ {{ range .People }}
+
+ {{ template "user/fragments/followCard"
+ (dict
+ "LoggedInUser" $.LoggedInUser
+ "UserDid" .UserDid
+ "Profile" .Profile
+ "FollowStatus" .FollowStatus
+ "FollowersCount" .FollowersCount
+ "FollowingCount" .FollowingCount) }}
+
+ {{ end }}
+
+ {{ end }}
+
+ {{ if .TrendingRepos }}
+
+ {{ range .TrendingRepos }}
+
+ {{ template "user/fragments/repoCard"
+ (list $ . true true
+ (dict
+ "SubjectAt" .RepoAt
+ "IsStarred" (index $.StarStatuses .RepoDid)
+ "StarCount" .RepoStats.StarCount
+ "RepoName" .Slug)) }}
+
+ {{ end }}
+
+ {{ end }}
+
+ {{ if not (or .People .TrendingRepos) }}
+ Nothing to suggest right now — you can explore once you're set up.
+ {{ end }}
+
+
+ {{ template "onboarding/back" "/welcome/profile" }}
+
+
+{{ end }}
diff --git a/appview/pages/templates/onboarding/welcome.html b/appview/pages/templates/onboarding/welcome.html
new file mode 100644
index 00000000..13977375
--- /dev/null
+++ b/appview/pages/templates/onboarding/welcome.html
@@ -0,0 +1,13 @@
+{{ define "title" }}Welcome · Tangled{{ end }}
+
+{{ define "content" }}
+ {{ if eq .Step 0 }}
+ {{ template "onboarding/profile" . }}
+ {{ else if eq .Step 1 }}
+ {{ template "onboarding/social" . }}
+ {{ else if eq .Step 2 }}
+ {{ template "onboarding/keys" . }}
+ {{ else }}
+ {{ template "onboarding/finish" . }}
+ {{ end }}
+{{ end }}
diff --git a/appview/pages/templates/user/fragments/editBio.html b/appview/pages/templates/user/fragments/editBio.html
index 8673a671..30406181 100644
--- a/appview/pages/templates/user/fragments/editBio.html
+++ b/appview/pages/templates/user/fragments/editBio.html
@@ -1,6 +1,6 @@
{{ define "user/fragments/editBio" }}