forked from tangled.org/core
this repo has no description

appview/{settings,pages}: rework settings ui

Preserves the routes, but borrows a similar style to that of repo
settings.

Signed-off-by: Anirudh Oppiliappan <anirudh@tangled.sh>

anirudh.fi fb4bff75 612bc427

verified
Changed files
+445 -204
appview
+8
appview/pages/funcmap.go
···
"github.com/go-enry/go-enry/v2"
"tangled.sh/tangled.sh/core/appview/filetree"
"tangled.sh/tangled.sh/core/appview/pages/markup"
+
"tangled.sh/tangled.sh/core/crypto"
)
func (p *Pages) funcMap() template.FuncMap {
···
"normalizeForHtmlId": func(s string) string {
// TODO: extend this to handle other cases?
return strings.ReplaceAll(s, ":", "_")
+
},
+
"sshFingerprint": func(pubKey string) string {
+
fp, err := crypto.SSHFingerprint(pubKey)
+
if err != nil {
+
return "error"
+
}
+
return fp
},
}
}
+25 -3
appview/pages/pages.go
···
return p.execute("timeline/timeline", w, params)
}
-
type SettingsParams struct {
+
type UserProfileSettingsParams struct {
+
LoggedInUser *oauth.User
+
Tabs []map[string]any
+
Tab string
+
}
+
+
func (p *Pages) UserProfileSettings(w io.Writer, params UserProfileSettingsParams) error {
+
return p.execute("user/settings/profile", w, params)
+
}
+
+
type UserKeysSettingsParams struct {
LoggedInUser *oauth.User
PubKeys []db.PublicKey
+
Tabs []map[string]any
+
Tab string
+
}
+
+
func (p *Pages) UserKeysSettings(w io.Writer, params UserKeysSettingsParams) error {
+
return p.execute("user/settings/keys", w, params)
+
}
+
+
type UserEmailsSettingsParams struct {
+
LoggedInUser *oauth.User
Emails []db.Email
+
Tabs []map[string]any
+
Tab string
}
-
func (p *Pages) Settings(w io.Writer, params SettingsParams) error {
-
return p.execute("settings", w, params)
+
func (p *Pages) UserEmailsSettings(w io.Writer, params UserEmailsSettingsParams) error {
+
return p.execute("user/settings/emails", w, params)
}
type KnotsParams struct {
-192
appview/pages/templates/settings.html
···
-
{{ define "title" }}settings{{ end }}
-
-
{{ define "content" }}
-
<div class="p-6">
-
<p class="text-xl font-bold dark:text-white">Settings</p>
-
</div>
-
<div class="flex flex-col">
-
{{ block "profile" . }} {{ end }}
-
{{ block "keys" . }} {{ end }}
-
{{ block "emails" . }} {{ end }}
-
</div>
-
{{ end }}
-
-
{{ define "profile" }}
-
<h2 class="text-sm font-bold py-2 px-6 uppercase dark:text-gray-300">profile</h2>
-
<section class="rounded bg-white dark:bg-gray-800 drop-shadow-sm px-6 py-4 mb-6 w-full lg:w-fit">
-
<dl class="grid grid-cols-[auto_1fr] gap-x-4 dark:text-gray-200">
-
{{ if .LoggedInUser.Handle }}
-
<dt class="font-bold">handle</dt>
-
<dd>@{{ .LoggedInUser.Handle }}</dd>
-
{{ end }}
-
<dt class="font-bold">did</dt>
-
<dd>{{ .LoggedInUser.Did }}</dd>
-
<dt class="font-bold">pds</dt>
-
<dd>{{ .LoggedInUser.Pds }}</dd>
-
</dl>
-
</section>
-
{{ end }}
-
-
{{ define "keys" }}
-
<h2 class="text-sm font-bold py-2 px-6 uppercase dark:text-gray-300">ssh keys</h2>
-
<section class="rounded bg-white dark:bg-gray-800 drop-shadow-sm px-6 py-4 mb-6 w-full lg:w-fit">
-
<p class="mb-8 dark:text-gray-300">SSH public keys added here will be broadcasted to knots that you are a member of, <br> allowing you to push to repositories there.</p>
-
<div id="key-list" class="flex flex-col gap-6 mb-8">
-
{{ range $index, $key := .PubKeys }}
-
<div class="grid grid-cols-[minmax(0,1fr)_auto] items-center gap-4">
-
<div class="flex flex-col gap-1">
-
<div class="inline-flex items-center gap-4">
-
{{ i "key" "w-3 h-3 dark:text-gray-300" }}
-
<p class="font-bold dark:text-white">{{ .Name }}</p>
-
</div>
-
<p class="text-sm text-gray-500 dark:text-gray-400">added {{ template "repo/fragments/time" .Created }}</p>
-
<div class="overflow-x-auto whitespace-nowrap flex-1 max-w-full">
-
<code class="text-sm text-gray-500 dark:text-gray-400">{{ .Key }}</code>
-
</div>
-
</div>
-
<button
-
class="btn text-red-500 hover:text-red-700 dark:text-red-400 dark:hover:text-red-300 gap-2 group"
-
title="Delete key"
-
hx-delete="/settings/keys?name={{urlquery .Name}}&rkey={{urlquery .Rkey}}&key={{urlquery .Key}}"
-
hx-confirm="Are you sure you want to delete the key '{{ .Name }}'?"
-
>
-
{{ i "trash-2" "w-5 h-5" }}
-
<span class="hidden md:inline">delete</span>
-
{{ i "loader-circle" "w-4 h-4 animate-spin hidden group-[.htmx-request]:inline" }}
-
</button>
-
</div>
-
{{ end }}
-
</div>
-
<form
-
hx-put="/settings/keys"
-
hx-indicator="#add-sshkey-spinner"
-
hx-swap="none"
-
class="max-w-2xl mb-8 space-y-4"
-
>
-
<input
-
type="text"
-
id="name"
-
name="name"
-
placeholder="key name"
-
required
-
class="w-full dark:bg-gray-700 dark:text-white dark:border-gray-600 dark:placeholder-gray-400"/>
-
-
<input
-
id="key"
-
name="key"
-
placeholder="ssh-rsa AAAAAA..."
-
required
-
class="w-full dark:bg-gray-700 dark:text-white dark:border-gray-600 dark:placeholder-gray-400"/>
-
-
<button class="btn dark:bg-gray-700 dark:text-white dark:hover:bg-gray-600 flex gap-2 items-center" type="submit">
-
<span>add key</span>
-
<span id="add-sshkey-spinner" class="group">
-
{{ i "loader-circle" "w-4 h-4 animate-spin hidden group-[.htmx-request]:inline" }}
-
</span>
-
</button>
-
-
<div id="settings-keys" class="error dark:text-red-400"></div>
-
</form>
-
</section>
-
{{ end }}
-
-
{{ define "emails" }}
-
<h2 class="text-sm font-bold py-2 px-6 uppercase dark:text-gray-300">email addresses</h2>
-
<section class="rounded bg-white dark:bg-gray-800 drop-shadow-sm px-6 py-4 mb-6 w-full lg:w-fit">
-
<p class="mb-8 dark:text-gray-300">Commits authored using emails listed here will be associated with your Tangled profile.</p>
-
<div id="email-list" class="flex flex-col gap-6 mb-8">
-
{{ range $index, $email := .Emails }}
-
<div class="grid grid-cols-[minmax(0,1fr)_auto] items-center gap-4">
-
<div class="flex flex-col gap-2">
-
<div class="inline-flex items-center gap-4">
-
{{ i "mail" "w-3 h-3 dark:text-gray-300" }}
-
<p class="font-bold dark:text-white">{{ .Address }}</p>
-
<div class="inline-flex items-center gap-1">
-
{{ if .Verified }}
-
<span class="text-xs bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200 px-2 py-1 rounded">verified</span>
-
{{ else }}
-
<span class="text-xs bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-200 px-2 py-1 rounded">unverified</span>
-
{{ end }}
-
{{ if .Primary }}
-
<span class="text-xs bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-200 px-2 py-1 rounded">primary</span>
-
{{ end }}
-
</div>
-
</div>
-
<p class="text-sm text-gray-500 dark:text-gray-400">added {{ template "repo/fragments/time" .CreatedAt }}</p>
-
</div>
-
<div class="flex gap-2 items-center">
-
{{ if not .Verified }}
-
<button
-
class="btn flex gap-2 dark:bg-gray-700 dark:text-white dark:hover:bg-gray-600"
-
hx-post="/settings/emails/verify/resend"
-
hx-swap="none"
-
href="#"
-
hx-vals='{"email": "{{ .Address }}"}'>
-
{{ i "rotate-cw" "w-5 h-5" }}
-
<span class="hidden md:inline">resend</span>
-
</button>
-
{{ end }}
-
{{ if and (not .Primary) .Verified }}
-
<a
-
class="text-sm dark:text-blue-400 dark:hover:text-blue-300"
-
hx-post="/settings/emails/primary"
-
hx-swap="none"
-
href="#"
-
hx-vals='{"email": "{{ .Address }}"}'>
-
set as primary
-
</a>
-
{{ end }}
-
{{ if not .Primary }}
-
<form
-
hx-delete="/settings/emails"
-
hx-confirm="Are you sure you wish to delete the email '{{ .Address }}'?"
-
hx-indicator="#delete-email-{{ $index }}-spinner"
-
>
-
<input type="hidden" name="email" value="{{ .Address }}">
-
<button
-
class="btn text-red-500 hover:text-red-700 dark:text-red-400 dark:hover:text-red-300 flex gap-2 items-center"
-
title="Delete email"
-
type="submit"
-
>
-
{{ i "trash-2" "w-5 h-5" }}
-
<span class="hidden md:inline">delete</span>
-
<span id="delete-email-{{ $index }}-spinner" class="group">
-
{{ i "loader-circle" "w-4 h-4 animate-spin hidden group-[.htmx-request]:inline" }}
-
</span>
-
</button>
-
</form>
-
{{ end }}
-
</div>
-
</div>
-
{{ end }}
-
</div>
-
<form
-
hx-put="/settings/emails"
-
hx-swap="none"
-
class="max-w-2xl mb-8 space-y-4"
-
hx-indicator="#add-email-spinner"
-
>
-
<input
-
type="email"
-
id="email"
-
name="email"
-
placeholder="your@email.com"
-
required
-
class="w-full dark:bg-gray-700 dark:text-white dark:border-gray-600 dark:placeholder-gray-400"
-
>
-
-
<button
-
class="btn dark:bg-gray-700 dark:text-white dark:hover:bg-gray-600 flex gap-2 items-center"
-
type="submit"
-
>
-
<span>add email</span>
-
<span id="add-email-spinner" class="group">
-
{{ i "loader-circle" "w-4 h-4 animate-spin hidden group-[.htmx-request]:inline" }}
-
</span>
-
</button>
-
-
<div id="settings-emails-error" class="error dark:text-red-400"></div>
-
<div id="settings-emails-success" class="success dark:text-green-400"></div>
-
</form>
-
</section>
-
{{ end }}
+94
appview/pages/templates/user/settings/emails.html
···
+
{{ define "title" }}{{ .Tab }} settings{{ end }}
+
+
{{ define "content" }}
+
<div class="p-6">
+
<p class="text-xl font-bold dark:text-white">Settings</p>
+
</div>
+
<div class="bg-white dark:bg-gray-800">
+
<section class="w-full grid grid-cols-1 md:grid-cols-4 gap-6 p-6">
+
<div class="col-span-1">
+
{{ template "user/settings/fragments/sidebar" . }}
+
</div>
+
<div class="col-span-1 md:col-span-3 flex flex-col gap-6">
+
{{ template "emailSettings" . }}
+
</div>
+
</section>
+
</div>
+
{{ end }}
+
+
{{ define "emailSettings" }}
+
<div class="grid grid-cols-1 md:grid-cols-3 gap-4 items-center">
+
<div class="col-span-1 md:col-span-2">
+
<h2 class="text-sm pb-2 uppercase font-bold">Email Addresses</h2>
+
<p class="text-gray-500 dark:text-gray-400">
+
Commits authored using emails listed here will be associated with your Tangled profile.
+
</p>
+
</div>
+
<div class="col-span-1 md:col-span-1 md:justify-self-end">
+
{{ template "addEmailButton" . }}
+
</div>
+
</div>
+
<div class="flex flex-col rounded border border-gray-200 dark:border-gray-700 divide-y divide-gray-200 dark:divide-gray-700 w-full">
+
{{ range .Emails }}
+
{{ template "user/settings/fragments/emailListing" (list $ .) }}
+
{{ else }}
+
<div class="flex items-center justify-center p-2 text-gray-500">
+
no emails added yet
+
</div>
+
{{ end }}
+
</div>
+
{{ end }}
+
+
{{ define "addEmailButton" }}
+
<button
+
class="btn flex items-center gap-2"
+
popovertarget="add-email-modal"
+
popovertargetaction="toggle">
+
{{ i "plus" "size-4" }}
+
add email
+
</button>
+
<div
+
id="add-email-modal"
+
popover
+
class="bg-white w-full md:w-96 dark:bg-gray-800 p-4 rounded border border-gray-200 dark:border-gray-700 drop-shadow dark:text-white backdrop:bg-gray-400/50 dark:backdrop:bg-gray-800/50">
+
{{ template "addEmailModal" . }}
+
</div>
+
{{ end}}
+
+
{{ define "addEmailModal" }}
+
<form
+
hx-put="/settings/emails"
+
hx-indicator="#spinner"
+
hx-swap="none"
+
class="flex flex-col gap-2"
+
>
+
<p class="uppercase p-0">ADD EMAIL</p>
+
<p class="text-sm text-gray-500 dark:text-gray-400">Commits using this email will be associated with your profile.</p>
+
<input
+
type="email"
+
id="email-address"
+
name="email"
+
required
+
placeholder="your@email.com"
+
class="w-full dark:bg-gray-700 dark:text-white dark:border-gray-600 dark:placeholder-gray-400"
+
/>
+
<div class="flex gap-2 pt-2">
+
<button
+
type="button"
+
popovertarget="add-email-modal"
+
popovertargetaction="hide"
+
class="btn w-1/2 flex items-center gap-2 text-red-500 hover:text-red-700 dark:text-red-400 dark:hover:text-red-300"
+
>
+
{{ i "x" "size-4" }} cancel
+
</button>
+
<button type="submit" class="btn w-1/2 flex items-center">
+
<span class="inline-flex gap-2 items-center">{{ i "plus" "size-4" }} add</span>
+
<span id="spinner" class="group">
+
{{ i "loader-circle" "ml-2 w-4 h-4 animate-spin hidden group-[.htmx-request]:inline" }}
+
</span>
+
</button>
+
</div>
+
<div id="settings-emails-error" class="text-red-500 dark:text-red-400"></div>
+
<div id="settings-emails-success" class="text-green-500 dark:text-green-400"></div>
+
</form>
+
{{ end }}
+62
appview/pages/templates/user/settings/fragments/emailListing.html
···
+
{{ define "user/settings/fragments/emailListing" }}
+
{{ $root := index . 0 }}
+
{{ $email := index . 1 }}
+
<div id="email-{{$email.Address}}" class="flex items-center justify-between p-2">
+
<div class="hover:no-underline flex flex-col gap-1 min-w-0 max-w-[80%]">
+
<div class="flex items-center gap-2">
+
{{ i "mail" "w-4 h-4 text-gray-500 dark:text-gray-400" }}
+
<span class="font-bold">
+
{{ $email.Address }}
+
</span>
+
<div class="inline-flex items-center gap-1">
+
{{ if $email.Verified }}
+
<span class="text-xs bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200 px-2 py-1 rounded">verified</span>
+
{{ else }}
+
<span class="text-xs bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-200 px-2 py-1 rounded">unverified</span>
+
{{ end }}
+
{{ if $email.Primary }}
+
<span class="text-xs bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-200 px-2 py-1 rounded">primary</span>
+
{{ end }}
+
</div>
+
</div>
+
<div class="flex text-sm flex-wrap text items-center gap-1 text-gray-500 dark:text-gray-400">
+
<span>added {{ template "repo/fragments/time" $email.CreatedAt }}</span>
+
</div>
+
</div>
+
<div class="flex gap-2 items-center">
+
{{ if not $email.Verified }}
+
<button
+
class="btn flex gap-2 text-sm px-2 py-1"
+
hx-post="/settings/emails/verify/resend"
+
hx-swap="none"
+
hx-vals='{"email": "{{ $email.Address }}"}'>
+
{{ i "rotate-cw" "w-4 h-4" }}
+
<span class="hidden md:inline">resend</span>
+
</button>
+
{{ end }}
+
{{ if and (not $email.Primary) $email.Verified }}
+
<button
+
class="btn text-sm px-2 py-1 text-blue-500 hover:text-blue-700 dark:text-blue-400 dark:hover:text-blue-300"
+
hx-post="/settings/emails/primary"
+
hx-swap="none"
+
hx-vals='{"email": "{{ $email.Address }}"}'>
+
set as primary
+
</button>
+
{{ end }}
+
{{ if not $email.Primary }}
+
<button
+
class="btn text-red-500 hover:text-red-700 dark:text-red-400 dark:hover:text-red-300 gap-2 group"
+
title="Delete email"
+
hx-delete="/settings/emails"
+
hx-swap="none"
+
hx-vals='{"email": "{{ $email.Address }}"}'
+
hx-confirm="Are you sure you want to delete the email {{ $email.Address }}?"
+
>
+
{{ i "trash-2" "w-5 h-5" }}
+
<span class="hidden md:inline">delete</span>
+
{{ i "loader-circle" "w-4 h-4 animate-spin hidden group-[.htmx-request]:inline" }}
+
</button>
+
{{ end }}
+
</div>
+
</div>
+
{{ end }}
+31
appview/pages/templates/user/settings/fragments/keyListing.html
···
+
{{ define "user/settings/fragments/keyListing" }}
+
{{ $root := index . 0 }}
+
{{ $key := index . 1 }}
+
<div id="key-{{$key.Name}}" class="flex items-center justify-between p-2">
+
<div class="hover:no-underline flex flex-col gap-1 text min-w-0 max-w-[80%]">
+
<div class="flex items-center gap-2">
+
<span>{{ i "key" "w-4" "h-4" }}</span>
+
<span class="font-bold">
+
{{ $key.Name }}
+
</span>
+
</div>
+
<span class="font-mono text-sm text-gray-500 dark:text-gray-400">
+
{{ sshFingerprint $key.Key }}
+
</span>
+
<div class="flex flex-wrap text-sm items-center gap-1 text-gray-500 dark:text-gray-400">
+
<span>added {{ template "repo/fragments/time" $key.Created }}</span>
+
</div>
+
</div>
+
<button
+
class="btn text-red-500 hover:text-red-700 dark:text-red-400 dark:hover:text-red-300 gap-2 group"
+
title="Delete key"
+
hx-delete="/settings/keys?name={{urlquery $key.Name}}&rkey={{urlquery $key.Rkey}}&key={{urlquery $key.Key}}"
+
hx-swap="none"
+
hx-confirm="Are you sure you want to delete the key {{ $key.Name }}?"
+
>
+
{{ i "trash-2" "w-5 h-5" }}
+
<span class="hidden md:inline">delete</span>
+
{{ i "loader-circle" "w-4 h-4 animate-spin hidden group-[.htmx-request]:inline" }}
+
</button>
+
</div>
+
{{ end }}
+16
appview/pages/templates/user/settings/fragments/sidebar.html
···
+
{{ define "user/settings/fragments/sidebar" }}
+
{{ $active := .Tab }}
+
{{ $tabs := .Tabs }}
+
<div class="sticky top-2 grid grid-cols-1 rounded border border-gray-200 dark:border-gray-700 divide-y divide-gray-200 dark:divide-gray-700 shadow-inner">
+
{{ $activeTab := "bg-white dark:bg-gray-700 drop-shadow-sm" }}
+
{{ $inactiveTab := "bg-gray-100 dark:bg-gray-800" }}
+
{{ range $tabs }}
+
<a href="/settings/{{.Name}}" class="no-underline hover:no-underline hover:bg-gray-100/25 hover:dark:bg-gray-700/25">
+
<div class="flex gap-3 items-center p-2 {{ if eq .Name $active }} {{ $activeTab }} {{ else }} {{ $inactiveTab }} {{ end }}">
+
{{ i .Icon "size-4" }}
+
{{ .Name }}
+
</div>
+
</a>
+
{{ end }}
+
</div>
+
{{ end }}
+101
appview/pages/templates/user/settings/keys.html
···
+
{{ define "title" }}{{ .Tab }} settings{{ end }}
+
+
{{ define "content" }}
+
<div class="p-6">
+
<p class="text-xl font-bold dark:text-white">Settings</p>
+
</div>
+
<div class="bg-white dark:bg-gray-800">
+
<section class="w-full grid grid-cols-1 md:grid-cols-4 gap-6 p-6">
+
<div class="col-span-1">
+
{{ template "user/settings/fragments/sidebar" . }}
+
</div>
+
<div class="col-span-1 md:col-span-3 flex flex-col gap-6">
+
{{ template "sshKeysSettings" . }}
+
</div>
+
</section>
+
</div>
+
{{ end }}
+
+
{{ define "sshKeysSettings" }}
+
<div class="grid grid-cols-1 md:grid-cols-3 gap-4 items-center">
+
<div class="col-span-1 md:col-span-2">
+
<h2 class="text-sm pb-2 uppercase font-bold">SSH Keys</h2>
+
<p class="text-gray-500 dark:text-gray-400">
+
SSH public keys added here will be broadcasted to knots that you are a member of,
+
allowing you to push to repositories there.
+
</p>
+
</div>
+
<div class="col-span-1 md:col-span-1 md:justify-self-end">
+
{{ template "addKeyButton" . }}
+
</div>
+
</div>
+
<div class="flex flex-col rounded border border-gray-200 dark:border-gray-700 divide-y divide-gray-200 dark:divide-gray-700 w-full">
+
{{ range .PubKeys }}
+
{{ template "user/settings/fragments/keyListing" (list $ .) }}
+
{{ else }}
+
<div class="flex items-center justify-center p-2 text-gray-500">
+
no keys added yet
+
</div>
+
{{ end }}
+
</div>
+
{{ end }}
+
+
{{ define "addKeyButton" }}
+
<button
+
class="btn flex items-center gap-2"
+
popovertarget="add-key-modal"
+
popovertargetaction="toggle">
+
{{ i "plus" "size-4" }}
+
add key
+
</button>
+
<div
+
id="add-key-modal"
+
popover
+
class="bg-white w-full md:w-96 dark:bg-gray-800 p-4 rounded border border-gray-200 dark:border-gray-700 drop-shadow dark:text-white backdrop:bg-gray-400/50 dark:backdrop:bg-gray-800/50">
+
{{ template "addKeyModal" . }}
+
</div>
+
{{ end}}
+
+
{{ define "addKeyModal" }}
+
<form
+
hx-put="/settings/keys"
+
hx-indicator="#spinner"
+
hx-swap="none"
+
class="flex flex-col gap-2"
+
>
+
<p class="uppercase p-0">ADD SSH KEY</p>
+
<p class="text-sm text-gray-500 dark:text-gray-400">SSH keys allow you to push to repositories in knots you're a member of.</p>
+
<input
+
type="text"
+
id="key-name"
+
name="name"
+
required
+
placeholder="key name"
+
class="w-full dark:bg-gray-700 dark:text-white dark:border-gray-600 dark:placeholder-gray-400"
+
/>
+
<textarea
+
type="text"
+
id="key-value"
+
name="key"
+
required
+
placeholder="ssh-rsa AAAAB3NzaC1yc2E..."
+
class="w-full dark:bg-gray-700 dark:text-white dark:border-gray-600 dark:placeholder-gray-400"></textarea>
+
<div class="flex gap-2 pt-2">
+
<button
+
type="button"
+
popovertarget="add-key-modal"
+
popovertargetaction="hide"
+
class="btn w-1/2 flex items-center gap-2 text-red-500 hover:text-red-700 dark:text-red-400 dark:hover:text-red-300"
+
>
+
{{ i "x" "size-4" }} cancel
+
</button>
+
<button type="submit" class="btn w-1/2 flex items-center">
+
<span class="inline-flex gap-2 items-center">{{ i "plus" "size-4" }} add</span>
+
<span id="spinner" class="group">
+
{{ i "loader-circle" "ml-2 w-4 h-4 animate-spin hidden group-[.htmx-request]:inline" }}
+
</span>
+
</button>
+
</div>
+
<div id="settings-keys" class="text-red-500 dark:text-red-400"></div>
+
</form>
+
{{ end }}
+64
appview/pages/templates/user/settings/profile.html
···
+
{{ define "title" }}{{ .Tab }} settings{{ end }}
+
+
{{ define "content" }}
+
<div class="p-6">
+
<p class="text-xl font-bold dark:text-white">Settings</p>
+
</div>
+
<div class="bg-white dark:bg-gray-800">
+
<section class="w-full grid grid-cols-1 md:grid-cols-4 gap-6 p-6">
+
<div class="col-span-1">
+
{{ template "user/settings/fragments/sidebar" . }}
+
</div>
+
<div class="col-span-1 md:col-span-3 flex flex-col gap-6">
+
{{ template "profileInfo" . }}
+
</div>
+
</section>
+
</div>
+
{{ end }}
+
+
{{ define "profileInfo" }}
+
<div class="grid grid-cols-1 md:grid-cols-3 gap-4 items-center">
+
<div class="col-span-1 md:col-span-2">
+
<h2 class="text-sm pb-2 uppercase font-bold">Profile</h2>
+
<p class="text-gray-500 dark:text-gray-400">
+
Your account information from your AT Protocol identity.
+
</p>
+
</div>
+
<div class="col-span-1 md:col-span-1 md:justify-self-end">
+
</div>
+
</div>
+
<div class="flex flex-col rounded border border-gray-200 dark:border-gray-700 divide-y divide-gray-200 dark:divide-gray-700 w-full">
+
<div class="flex items-center justify-between p-4">
+
<div class="hover:no-underline flex flex-col gap-1 min-w-0 max-w-[80%]">
+
{{ if .LoggedInUser.Handle }}
+
<span class="font-bold">
+
@{{ .LoggedInUser.Handle }}
+
</span>
+
<div class="flex flex-wrap text items-center gap-1 text-gray-500 dark:text-gray-400">
+
<span>Handle</span>
+
</div>
+
{{ end }}
+
</div>
+
</div>
+
<div class="flex items-center justify-between p-4">
+
<div class="hover:no-underline flex flex-col gap-1 min-w-0 max-w-[80%]">
+
<span class="font-mono text-xs">
+
{{ .LoggedInUser.Did }}
+
</span>
+
<div class="flex flex-wrap text items-center gap-1 text-gray-500 dark:text-gray-400">
+
<span>Decentralized Identifier (DID)</span>
+
</div>
+
</div>
+
</div>
+
<div class="flex items-center justify-between p-4">
+
<div class="hover:no-underline flex flex-col gap-1 min-w-0 max-w-[80%]">
+
<span class="font-bold">
+
{{ .LoggedInUser.Pds }}
+
</span>
+
<div class="flex flex-wrap text items-center gap-1 text-gray-500 dark:text-gray-400">
+
<span>Personal Data Server (PDS)</span>
+
</div>
+
</div>
+
</div>
+
</div>
+
{{ end }}
+44 -9
appview/settings/settings.go
···
Config *config.Config
}
+
type tab = map[string]any
+
+
var (
+
settingsTabs []tab = []tab{
+
{"Name": "profile", "Icon": "user"},
+
{"Name": "keys", "Icon": "key"},
+
{"Name": "emails", "Icon": "mail"},
+
}
+
)
+
func (s *Settings) Router() http.Handler {
r := chi.NewRouter()
r.Use(middleware.AuthMiddleware(s.OAuth))
-
r.Get("/", s.settings)
+
// settings pages
+
r.Get("/", s.profileSettings)
+
r.Get("/profile", s.profileSettings)
r.Route("/keys", func(r chi.Router) {
+
r.Get("/", s.keysSettings)
r.Put("/", s.keys)
r.Delete("/", s.keys)
})
r.Route("/emails", func(r chi.Router) {
+
r.Get("/", s.emailsSettings)
r.Put("/", s.emails)
r.Delete("/", s.emails)
r.Get("/verify", s.emailsVerify)
···
return r
}
-
func (s *Settings) settings(w http.ResponseWriter, r *http.Request) {
+
func (s *Settings) profileSettings(w http.ResponseWriter, r *http.Request) {
+
user := s.OAuth.GetUser(r)
+
+
s.Pages.UserProfileSettings(w, pages.UserProfileSettingsParams{
+
LoggedInUser: user,
+
Tabs: settingsTabs,
+
Tab: "profile",
+
})
+
}
+
+
func (s *Settings) keysSettings(w http.ResponseWriter, r *http.Request) {
user := s.OAuth.GetUser(r)
pubKeys, err := db.GetPublicKeysForDid(s.Db, user.Did)
if err != nil {
log.Println(err)
}
+
s.Pages.UserKeysSettings(w, pages.UserKeysSettingsParams{
+
LoggedInUser: user,
+
PubKeys: pubKeys,
+
Tabs: settingsTabs,
+
Tab: "keys",
+
})
+
}
+
+
func (s *Settings) emailsSettings(w http.ResponseWriter, r *http.Request) {
+
user := s.OAuth.GetUser(r)
emails, err := db.GetAllEmails(s.Db, user.Did)
if err != nil {
log.Println(err)
}
-
s.Pages.Settings(w, pages.SettingsParams{
+
s.Pages.UserEmailsSettings(w, pages.UserEmailsSettingsParams{
LoggedInUser: user,
-
PubKeys: pubKeys,
Emails: emails,
+
Tabs: settingsTabs,
+
Tab: "emails",
})
}
···
return
}
-
s.Pages.HxLocation(w, "/settings")
+
s.Pages.HxLocation(w, "/settings/emails")
return
}
}
···
return
}
-
http.Redirect(w, r, "/settings", http.StatusSeeOther)
+
http.Redirect(w, r, "/settings/emails", http.StatusSeeOther)
}
func (s *Settings) emailsVerifyResend(w http.ResponseWriter, r *http.Request) {
···
return
}
-
s.Pages.HxLocation(w, "/settings")
+
s.Pages.HxLocation(w, "/settings/emails")
}
func (s *Settings) keys(w http.ResponseWriter, r *http.Request) {
···
return
}
-
s.Pages.HxLocation(w, "/settings")
+
s.Pages.HxLocation(w, "/settings/keys")
return
case http.MethodDelete:
···
}
log.Println("deleted successfully")
-
s.Pages.HxLocation(w, "/settings")
+
s.Pages.HxLocation(w, "/settings/keys")
return
}
}