Add a profile dropdown menu

This commit is contained in:
Ben Visness 2024-07-02 22:58:33 -05:00
parent a646dddec0
commit dffd1c94b5
9 changed files with 58 additions and 53 deletions

View File

@ -8298,7 +8298,7 @@ header .header-nav > .root-item > a {
display: block;
padding: var(--spacing-3);
}
header .header-nav .submenu {
header .submenu {
display: flex;
flex-direction: column;
position: absolute;
@ -8309,16 +8309,16 @@ header .header-nav .submenu {
border-width: 1px;
border-top-width: 0;
}
header .header-nav .submenu > a {
header .submenu > a {
padding: var(--spacing-2) var(--spacing-3);
display: block;
white-space: nowrap;
z-index: 1;
}
header .header-nav .root-item:not(:hover):not(.clicked) > .submenu {
header .root-item:not(:hover):not(.clicked) > .submenu {
display: none;
}
header .header-nav .root-item.clicked .svgicon {
header .root-item.clicked .svgicon {
transform: rotate(180deg);
}
header:not(.clicked) .root-item:not(:hover) > .submenu,

View File

@ -67,7 +67,7 @@ func TestLoginWithDiscord(t *testing.T) {
}
func TestLogoutAction(t *testing.T) {
AssertRegexMatch(t, BuildLogoutAction(""), RegexLogoutAction, nil)
AssertRegexMatch(t, BuildLogoutAction(""), RegexLogout, nil)
}
func TestRegister(t *testing.T) {

View File

@ -219,7 +219,7 @@ func BuildLoginWithDiscord(redirectTo string) string {
return Url("/login-with-discord", []Q{{Name: "redirect", Value: redirectTo}})
}
var RegexLogoutAction = regexp.MustCompile("^/logout$")
var RegexLogout = regexp.MustCompile("^/logout$")
func BuildLogoutAction(redir string) string {
defer CatchPanic()

View File

@ -144,34 +144,34 @@ header {
display: block;
padding: var(--spacing-3);
}
}
.submenu {
display: flex;
flex-direction: column;
position: absolute;
z-index: 100;
min-width: 8rem;
background-color: var(--c3);
border-style: solid;
border-width: 1px;
border-top-width: 0;
.submenu {
display: flex;
flex-direction: column;
position: absolute;
z-index: 100;
min-width: 8rem;
background-color: var(--c3);
border-style: solid;
border-width: 1px;
border-top-width: 0;
>a {
padding: var(--spacing-2) var(--spacing-3);
display: block;
white-space: nowrap;
z-index: 1;
}
>a {
padding: var(--spacing-2) var(--spacing-3);
display: block;
white-space: nowrap;
z-index: 1;
}
}
.root-item {
&:not(:hover):not(.clicked)>.submenu {
display: none;
}
.root-item {
&:not(:hover):not(.clicked)>.submenu {
display: none;
}
&.clicked .svgicon {
transform: rotate(180deg);
}
&.clicked .svgicon {
transform: rotate(180deg);
}
}

View File

@ -39,13 +39,22 @@
</div>
</div>
</div>
<a class="db {{ if .User }}pv2 ph3{{ else }}pa3{{ end }} lh-solid flex f6 {{ if not .User }}bl{{ end }}" href="{{ or .Header.UserProfileUrl .LoginPageUrl }}">
{{ with .User }}
<img class="avatar avatar-user" src="{{ .AvatarUrl }}">
{{ else }}
Log In
<div class="root-item f6">
<a class="db {{ if .User }}pv2 ph3{{ else }}pa3{{ end }} lh-solid flex f6 {{ if not .User }}bl{{ end }}" href="{{ or .Header.UserProfileUrl .LoginPageUrl }}">
{{ with .User }}
<img class="avatar avatar-user" src="{{ .AvatarUrl }}">
{{ else }}
Log In
{{ end }}
</a>
{{ if .User }}
<div class="submenu right-0" id="profile-submenu">
<a href="{{ .Header.UserProfileUrl }}">Profile</a>
<a href="{{ .Header.UserSettingsUrl }}">Settings</a>
<a href="{{ .Header.LogoutUrl }}">Log Out</a>
</div>
{{ end }}
</a>
</div>
</header>
<script type="text/javascript">
document.addEventListener("DOMContentLoaded", function() {

View File

@ -9,7 +9,7 @@
<a class="dib pv2 pl2" href="{{ .Header.UserProfileUrl }}">{{ .User.Username }}</a>
<a class="dib pv2 pr2" href="{{ .Header.UserSettingsUrl }}">(settings)</a>
</div>
<a class="pa2" href="{{ .Header.LogoutActionUrl }}"><span class="icon-logout"></span> Log Out</a>
<a class="pa2" href="{{ .Header.LogoutUrl }}"><span class="icon-logout"></span> Log Out</a>
{{ else }}
<a class="pa2" id="register-link" href="{{ .Header.RegisterUrl }}">Register</a>
<a class="pa2" id="login-link" href="{{ .LoginPageUrl }}">Log in</a>

View File

@ -40,14 +40,12 @@ func (bd *BaseData) AddImmediateNotice(class, content string) {
}
type Header struct {
AdminUrl string
UserProfileUrl string
UserSettingsUrl string
LoginActionUrl string
LogoutActionUrl string
ForgotPasswordUrl string
RegisterUrl string
LoginWithDiscordUrl string
AdminUrl string
UserProfileUrl string
UserSettingsUrl string
LogoutUrl string
ForgotPasswordUrl string
RegisterUrl string
HMNHomepageUrl string
ProjectIndexUrl string

View File

@ -64,13 +64,11 @@ func getBaseData(c *RequestContext, title string, breadcrumbs []templates.Breadc
IsProjectPage: !project.IsHMN(),
Header: templates.Header{
AdminUrl: hmnurl.BuildAdminApprovalQueue(), // TODO(asaf): Replace with general-purpose admin page
UserSettingsUrl: hmnurl.BuildUserSettings(""),
LoginActionUrl: hmnurl.BuildLoginAction(c.FullUrl()),
LogoutActionUrl: hmnurl.BuildLogoutAction(c.FullUrl()),
ForgotPasswordUrl: hmnurl.BuildRequestPasswordReset(),
RegisterUrl: hmnurl.BuildRegister(""),
LoginWithDiscordUrl: hmnurl.BuildLoginWithDiscord(c.FullUrl()),
AdminUrl: hmnurl.BuildAdminApprovalQueue(), // TODO(asaf): Replace with general-purpose admin page
UserSettingsUrl: hmnurl.BuildUserSettings(""),
LogoutUrl: hmnurl.BuildLogoutAction(c.FullUrl()),
ForgotPasswordUrl: hmnurl.BuildRequestPasswordReset(),
RegisterUrl: hmnurl.BuildRegister(""),
HMNHomepageUrl: hmnurl.BuildHomepage(),
ProjectIndexUrl: hmnurl.BuildProjectIndex(),

View File

@ -137,7 +137,7 @@ func NewWebsiteRoutes(conn *pgxpool.Pool) http.Handler {
hmnOnly.GET(hmnurl.RegexOldHome, Index)
hmnOnly.POST(hmnurl.RegexLoginAction, securityTimerMiddleware(time.Millisecond*100, Login))
hmnOnly.GET(hmnurl.RegexLogoutAction, Logout)
hmnOnly.GET(hmnurl.RegexLogout, Logout)
hmnOnly.GET(hmnurl.RegexLoginPage, LoginPage)
hmnOnly.GET(hmnurl.RegexLoginWithDiscord, LoginWithDiscord)