show loading status in user interface

This commit is contained in:
Brad Rydzewski 2017-09-20 12:29:57 -07:00
parent ae51e9d1b9
commit ec6016062b
5 changed files with 108 additions and 17 deletions

View file

@ -2,6 +2,7 @@ package main
import ( import (
"fmt" "fmt"
"time"
"github.com/cncd/queue" "github.com/cncd/queue"
"github.com/dimfeld/httptreemux" "github.com/dimfeld/httptreemux"
@ -176,11 +177,10 @@ func setupCoding(c *cli.Context) (remote.Remote, error) {
func setupTree(c *cli.Context) *httptreemux.ContextMux { func setupTree(c *cli.Context) *httptreemux.ContextMux {
tree := httptreemux.NewContextMux() tree := httptreemux.NewContextMux()
if path := c.String("www"); path == "" { web.New(
web.New().Register(tree) web.WithDir(c.String("www")),
} else { web.WithSync(time.Hour*72),
web.FromPath(path).Register(tree) ).Register(tree)
}
return tree return tree
} }

37
server/web/opts.go Normal file
View file

@ -0,0 +1,37 @@
package web
import "time"
// Options defines website handler options.
type Options struct {
sync time.Duration
path string
docs string
}
// Option configures the website handler.
type Option func(*Options)
// WithSync configures the website hanlder with the duration value
// used to determine if the user account requires synchronization.
func WithSync(d time.Duration) Option {
return func(o *Options) {
o.sync = d
}
}
// WithDir configures the website hanlder with the directory value
// used to serve the website from the local filesystem.
func WithDir(s string) Option {
return func(o *Options) {
o.path = s
}
}
// WithDocs configures the website hanlder with the documentation
// website address, which should be included in the user interface.
func WithDocs(s string) Option {
return func(o *Options) {
o.docs = s
}
}

30
server/web/opts_test.go Normal file
View file

@ -0,0 +1,30 @@
package web
import (
"testing"
"time"
)
func TestWithSync(t *testing.T) {
opts := new(Options)
WithSync(time.Minute)(opts)
if got, want := opts.sync, time.Minute; got != want {
t.Errorf("Want sync duration %v, got %v", want, got)
}
}
func TestWithDir(t *testing.T) {
opts := new(Options)
WithDir("/tmp/www")(opts)
if got, want := opts.path, "/tmp/www"; got != want {
t.Errorf("Want www directory %q, got %q", want, got)
}
}
func TestWithDocs(t *testing.T) {
opts := new(Options)
WithDocs("http://docs.drone.io")(opts)
if got, want := opts.docs, "http://docs.drone.io"; got != want {
t.Errorf("Want documentation url %q, got %q", want, got)
}
}

View file

@ -68,6 +68,7 @@ const partials = `
{{ if .user }} {{ if .user }}
<script> <script>
window.DRONE_USER = {{ json .user }}; window.DRONE_USER = {{ json .user }};
window.DRONE_SYNC = {{ .syncing }};
</script> </script>
{{ end }} {{ end }}
{{end}} {{end}}
@ -83,4 +84,12 @@ const partials = `
{{define "version"}} {{define "version"}}
<meta name="version" content="{{ .version }}"> <meta name="version" content="{{ .version }}">
{{end}} {{end}}
{{define "docs"}}
{{ if .docs -}}
<script>
window.DRONE_DOCS = "{{ .docs }}"
</script>
{{- end }}
{{end}}
` `

View file

@ -25,32 +25,42 @@ type Endpoint interface {
} }
// New returns the default website endpoint. // New returns the default website endpoint.
func New() Endpoint { func New(opt ...Option) Endpoint {
opts := new(Options)
for _, f := range opt {
f(opts)
}
if opts.path != "" {
return fromPath(opts)
}
return &website{ return &website{
fs: dist.New(), fs: dist.New(),
templ: mustCreateTemplate( opts: opts,
tmpl: mustCreateTemplate(
string(dist.MustLookup("/index.html")), string(dist.MustLookup("/index.html")),
), ),
} }
} }
// FromPath returns the website endpoint that func fromPath(opts *Options) *website {
// serves the webpage form disk at path p. f := filepath.Join(opts.path, "index.html")
func FromPath(p string) Endpoint {
f := filepath.Join(p, "index.html")
b, err := ioutil.ReadFile(f) b, err := ioutil.ReadFile(f)
if err != nil { if err != nil {
panic(err) panic(err)
} }
return &website{ return &website{
fs: http.Dir(p), fs: http.Dir(opts.path),
templ: mustCreateTemplate(string(b)), tmpl: mustCreateTemplate(string(b)),
opts: opts,
} }
} }
type website struct { type website struct {
fs http.FileSystem opts *Options
templ *template.Template fs http.FileSystem
tmpl *template.Template
} }
func (w *website) Register(mux *httptreemux.ContextMux) { func (w *website) Register(mux *httptreemux.ContextMux) {
@ -72,14 +82,19 @@ func (w *website) handleIndex(rw http.ResponseWriter, r *http.Request) {
user.Login, user.Login,
).Sign(user.Hash) ).Sign(user.Hash)
} }
var syncing bool
if user != nil {
syncing = time.Unix(user.Synced, 0).Add(w.opts.sync).Before(time.Now())
}
params := map[string]interface{}{ params := map[string]interface{}{
"user": user, "user": user,
"csrf": csrf, "csrf": csrf,
"syncing": syncing,
"version": version.Version.String(), "version": version.Version.String(),
} }
rw.Header().Set("Content-Type", "text/html; charset=UTF-8") rw.Header().Set("Content-Type", "text/html; charset=UTF-8")
w.templ.Execute(rw, params) w.tmpl.Execute(rw, params)
} }
func setupCache(h http.Handler) http.Handler { func setupCache(h http.Handler) http.Handler {