Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Start styling privacy modes ui #126

Merged
merged 6 commits into from
Apr 9, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions roomdb/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,16 @@ func (pm PrivacyMode) IsValid() error {

func ParsePrivacyMode(val string) PrivacyMode {
switch val {
case "ModeOpen":
fallthrough
case "open":
return ModeOpen
case "ModeCommunity":
fallthrough
case "community":
return ModeCommunity
case "ModeRestricted":
fallthrough
case "restricted":
return ModeRestricted
default:
Expand Down
87 changes: 87 additions & 0 deletions web/handlers/admin/dashboard.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
// SPDX-License-Identifier: MIT

package admin

import (
// "errors"
"fmt"
"net/http"

"go.mindeco.de/http/render"

"github.com/gorilla/csrf"
"github.com/ssb-ngi-pointer/go-ssb-room/roomdb"
// weberrors "github.com/ssb-ngi-pointer/go-ssb-room/web/errors"
"github.com/ssb-ngi-pointer/go-ssb-room/roomstate"
"github.com/ssb-ngi-pointer/go-ssb-room/web"
"github.com/ssb-ngi-pointer/go-ssb-room/web/router"
)

type dashboardHandler struct {
r *render.Renderer
roomState *roomstate.Manager
dbs Databases
}

func (h dashboardHandler) overview(w http.ResponseWriter, req *http.Request) (interface{}, error) {
onlineRefs := h.roomState.List()
onlineCount := len(onlineRefs)
memberCount, err := h.dbs.Members.Count(req.Context())
if err != nil {
return nil, fmt.Errorf("failed to count members: %w", err)
}
inviteCount, err := h.dbs.Invites.Count(req.Context())
if err != nil {
return nil, fmt.Errorf("failed to count invites: %w", err)
}
deniedCount, err := h.dbs.DeniedKeys.Count(req.Context())
if err != nil {
return nil, fmt.Errorf("failed to count denied keys: %w", err)
}
privacyModes := []roomdb.PrivacyMode{roomdb.ModeOpen, roomdb.ModeCommunity, roomdb.ModeRestricted}
currentMode, err := h.dbs.Config.GetPrivacyMode(req.Context())
if err != nil {
return nil, fmt.Errorf("failed to retrieve current privacy mode: %w", err)
}

return map[string]interface{}{
"OnlineRefs": onlineRefs,
"OnlineCount": onlineCount,
"MemberCount": memberCount,
"InviteCount": inviteCount,
"DeniedCount": deniedCount,
"CurrentMode": currentMode,
"PrivacyModes": privacyModes,
csrf.TemplateTag: csrf.TemplateField(req),
}, nil
}

func (h dashboardHandler) setPrivacy(w http.ResponseWriter, req *http.Request) {
if req.Method != "POST" {
// TODO: proper error type
h.r.Error(w, req, http.StatusBadRequest, fmt.Errorf("bad request"))
return
}

if err := req.ParseForm(); err != nil {
// TODO: proper error type
h.r.Error(w, req, http.StatusBadRequest, fmt.Errorf("bad request: %w", err))
return
}

pmValue := req.Form.Get("privacy_mode")

pm := roomdb.ParsePrivacyMode(pmValue)
if pm == roomdb.ModeUnknown {
h.r.Error(w, req, http.StatusBadRequest, fmt.Errorf("unknown privacy mode was being set: %v", pmValue))
}

err := h.dbs.Config.SetPrivacyMode(req.Context(), pm)
if err != nil {
h.r.Error(w, req, http.StatusBadRequest, fmt.Errorf("something went wrong when setting the privacy mode: %w", err))
}

urlTo := web.NewURLTo(router.CompleteApp())
dashboard := urlTo(router.AdminDashboard).String()
http.Redirect(w, req, dashboard, http.StatusFound)
}
34 changes: 10 additions & 24 deletions web/handlers/admin/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ var HTMLTemplates = []string{
"admin/members-remove-confirm.tmpl",
}

// Databases is an option struct that encapsualtes the required database services
// Databases is an option struct that encapsulates the required database services
type Databases struct {
Aliases roomdb.AliasesService
Config roomdb.RoomConfig
Expand All @@ -61,31 +61,17 @@ func Handler(
dbs Databases,
) http.Handler {
mux := &http.ServeMux{}

// TODO: configure 404 handler
var dashboardHandler = dashboardHandler{
r: r,
dbs: dbs,
roomState: roomState,
}

mux.HandleFunc("/dashboard", r.HTML("admin/dashboard.tmpl", dashboardHandler.overview))
mux.HandleFunc("/dashboard/set-privacy", dashboardHandler.setPrivacy)

mux.HandleFunc("/dashboard", r.HTML("admin/dashboard.tmpl", func(rw http.ResponseWriter, req *http.Request) (interface{}, error) {
onlineRefs := roomState.List()
onlineCount := len(onlineRefs)
memberCount, err := dbs.Members.Count(req.Context())
if err != nil {
return nil, fmt.Errorf("failed to count members: %w", err)
}
inviteCount, err := dbs.Invites.Count(req.Context())
if err != nil {
return nil, fmt.Errorf("failed to count invites: %w", err)
}
deniedCount, err := dbs.DeniedKeys.Count(req.Context())
if err != nil {
return nil, fmt.Errorf("failed to count denied keys: %w", err)
}
return struct {
OnlineRefs []string
OnlineCount int
MemberCount uint
InviteCount uint
DeniedCount uint
}{onlineRefs, onlineCount, memberCount, inviteCount, deniedCount}, nil
}))
mux.HandleFunc("/menu", r.HTML("admin/menu.tmpl", func(w http.ResponseWriter, req *http.Request) (interface{}, error) {
return map[string]interface{}{}, nil
}))
Expand Down
15 changes: 14 additions & 1 deletion web/i18n/defaults/active.en.toml
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,19 @@ NoticeNews = "News"
NoticeDescription = "Description"
NoticePrivacyPolicy = "Privacy Policy"

ModeOpen = "Open"
ModeCommunity = "Community"
ModeRestricted = "Restricted"

SetPrivacyModeTitle = "Set Privacy Mode"
PrivacyModesTitle = "Privacy Modes"
RoomsSpecification = "rooms 2 specification"
ExplanationPrivacyModes = "The privacy mode of this room determines who can create invites and who can connect to the room. For more information, see the"
ExplanationOpen = "Open invite codes, anyone may connect"
ExplanationCommunity = "Members can create invites, anyone may connect"
ExplanationRestricted = "Only admins/mods can create invites, only members may connect"


[MemberCount]
description = "Number of members"
one = "1 member"
Expand All @@ -129,4 +142,4 @@ other = "There are {{.Count}} people in the Room"
[AdminInvitesCount]
description = "the number of invites that are not yet claimed"
one = "1 invite still unclaimed"
other = "{{.Count}} invites still unclaimed"
other = "{{.Count}} invites still unclaimed"
7 changes: 5 additions & 2 deletions web/router/admin.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ import "github.com/gorilla/mux"

// constant names for the named routes
const (
AdminDashboard = "admin:dashboard"
AdminMenu = "admin:menu"
AdminDashboard = "admin:dashboard"
AdminDashboardSetPrivacy = "admin:dashboard:set-privacy"
AdminMenu = "admin:menu"

AdminAliasesRevokeConfirm = "admin:aliases:revoke:confirm"
AdminAliasesRevoke = "admin:aliases:revoke"
Expand Down Expand Up @@ -43,6 +44,8 @@ func Admin(m *mux.Router) *mux.Router {
}

m.Path("/dashboard").Methods("GET").Name(AdminDashboard)
m.Path("/dashboard/set-privacy").Methods("POST").Name(AdminDashboardSetPrivacy)

m.Path("/menu").Methods("GET").Name(AdminMenu)

m.Path("/aliases/revoke/confirm").Methods("GET").Name(AdminAliasesRevokeConfirm)
Expand Down
51 changes: 50 additions & 1 deletion web/templates/admin/dashboard.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -85,5 +85,54 @@
</div>
{{end}}
</div>
<div class="max-w-lg">
<h2 class="text-xl tracking-tight font-bold text-black mt-2 mb-2">{{i18n "PrivacyModesTitle"}}</h2>
<p class="mb-4">
{{ i18n "ExplanationPrivacyModes" }}
<a class="text-pink-600 underline" href="https://ssb-ngi-pointer.github.io/rooms2/#privacy-modes">{{ i18n "RoomsSpecification" }}</a>.
</p>
<h3 class="text-gray-400 text-sm font-bold mb-2">{{ i18n "SetPrivacyModeTitle" }}</h3>
<details class="mb-8 self-start w-96" id="change-privacy">
<summary class="px-3 py-1 w-96 rounded shadow bg-white ring-1 ring-gray-300 hover:bg-gray-100 cursor-pointer">
{{ i18n .CurrentMode.String }}
</summary>

<div class="absolute w-96 z-10 bg-white mt-2 shadow-xl ring-1 ring-gray-200 rounded divide-y flex flex-col items-stretch overflow-hidden">
{{ range .PrivacyModes }}
{{ if ne . $.CurrentMode }}
<form
action="{{urlTo "admin:dashboard:set-privacy" }}"
method="POST"
>
{{$.csrfField}}
<input type="hidden" name="privacy_mode" value="{{.}}">
<input
type="submit"
value="{{ i18n .String }}"
class="pl-10 pr-3 py-2 w-full text-left bg-white text-gray-700 hover:text-gray-900 hover:bg-gray-50 cursor-pointer"
/>
</form>
{{ else }}
<div class="pr-3 py-2 text-gray-600 flex flex-row items-center cursor-default">
<div class="w-10 flex flex-row items-center justify-center">
<svg class="w-4 h-4" viewBox="0 0 24 24">
<path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z" />
</svg>
</div>
<span>{{ i18n .String }}</span>
</div>
{{end}}
{{end}}
</div>
</details>
<div class="grid max-w-lg grid-cols-3 gap-y-2 mb-8">
<div class="text-xl text-gray-500 font-bold">Open</div>
<div class="text-md col-span-2 italic">{{ i18n "ExplanationOpen" }}</div>
<div class="text-xl text-gray-500 font-bold">Community</div>
<div class="text-md col-span-2 italic">{{ i18n "ExplanationCommunity" }}</div>
<div class="text-xl text-gray-500 font-bold">Restricted</div>
<div class="text-md col-span-2 italic">{{ i18n "ExplanationRestricted" }}</div>
</div>
</div>
</div>
{{end}}
{{end}}