Skip to content

Commit

Permalink
Render HTML on frontend.
Browse files Browse the repository at this point in the history
This is a large change that primarily moves HTML rendering and display
logic from backend to frontend (#67).

Previously, the HTML for displaying updates was rendered on backend and
streamed to browser. This worked surprisingly well and got me far, but
in order to be able to have more fine grained control over frontend
details, it was no longer viable to keep doing that. Now, the HTML is
fully rendered on frontend, and most of the logic resides on the
frontend. The backend provides services to the frontend. See issue #67
for full rationale why this is desired.

Implement a long-standing feature request of having an "Update All"
button (#6). This is both made possible and easy thanks to the frontend
HTML rendering.

This change partially helps #63, but also enables potential future
changes to help it even more.

In general, the move to frontend HTML rendering will help potentially
achieve some of enhancements described in #8.

Close #66 by removing the popup altogether. It wasn't well implemented,
so it's better to remove. In the future, a better replacement
implementation of the notification (without the modal popup) can be
considered.

Resolves #67.
Closes #66.
Helps #63.
Helps #8.
Resolves #6.
  • Loading branch information
dmitshur committed Apr 11, 2017
1 parent 850f700 commit f0ae285
Show file tree
Hide file tree
Showing 14 changed files with 1,002 additions and 387 deletions.
76 changes: 0 additions & 76 deletions assets/_data/script/main.go

This file was deleted.

6 changes: 0 additions & 6 deletions assets/_data/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,6 @@ h2 {
margin-bottom: 40px;
}

.disabled {
pointer-events: none;
cursor: default;
color: gray;
text-decoration: none;
}
span.smaller {
font-size: 11px;
color: gray;
Expand Down
3 changes: 2 additions & 1 deletion assets/assets.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ import (

// Assets contains assets for Go Package Store.
var Assets = union.New(map[string]http.FileSystem{
"/assets": gopherjs_http.NewFS(http.Dir(importPathToDir("github.com/shurcooL/Go-Package-Store/assets/_data"))),
"/assets": gopherjs_http.NewFS(http.Dir(importPathToDir("github.com/shurcooL/Go-Package-Store/assets/_data"))),
"/frontend.js": gopherjs_http.Package("github.com/shurcooL/Go-Package-Store/frontend"),
})

func importPathToDir(importPath string) string {
Expand Down
194 changes: 182 additions & 12 deletions cmd/Go-Package-Store/dev.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,43 +4,213 @@ package main

import (
"errors"
"io"
"net/http"
"time"

"github.com/shurcooL/Go-Package-Store"
gpscomponent "github.com/shurcooL/Go-Package-Store/component"
"github.com/shurcooL/Go-Package-Store/workspace"
"github.com/shurcooL/htmlg"
"github.com/shurcooL/httperror"
)

import _ "net/http/pprof"

const production = false

func init() {
http.HandleFunc("/mock.html", mockHandler)
http.Handle("/mock.html", errorHandler(mockHandler))
http.Handle("/component.html", errorHandler(componentHandler))
}

func mockHandler(w http.ResponseWriter, req *http.Request) {
func mockHandler(w http.ResponseWriter, req *http.Request) error {
if req.Method != "GET" {
httperror.HandleMethod(w, httperror.Method{Allowed: []string{"GET"}})
return
return httperror.Method{Allowed: []string{"GET"}}
}

// Reset the pipeline and populate it with mock repo presentations,
// complete with artificial delays (to simulate processing time).
c.pipeline = workspace.NewPipeline(wd)
go func() {
for _, repoPresentation := range mockRepoPresentations {
repoPresentation := repoPresentation
time.Sleep(time.Second)
c.pipeline.AddPresented(&repoPresentation)
for _, rp := range mockWorkspaceRPs {
time.Sleep(5 * time.Second)
rp := rp
c.pipeline.AddPresented(&rp)
}
time.Sleep(time.Second)
time.Sleep(5 * time.Second)
c.pipeline.Done()
}()

mainHandler(w, req)
return indexHandler(w, req)
}

func componentHandler(w http.ResponseWriter, req *http.Request) error {
if req.Method != "GET" {
return httperror.Method{Allowed: []string{"GET"}}
}

w.Header().Set("Content-Type", "text/html; charset=utf-8")

_, err := io.WriteString(w, `<html>
<head>
<title>Go Package Store</title>
<link href="/assets/style.css" rel="stylesheet" type="text/css" />
</head>
<body>`)
if err != nil {
return err
}

err = htmlg.RenderComponents(w, gpscomponent.Header{})
if err != nil {
return err
}

_, err = io.WriteString(w, `<div class="center-max-width"><div class="content">`)
if err != nil {
return err
}

err = htmlg.RenderComponents(w, gpscomponent.UpdatesHeader{
RPs: nil,
CheckingUpdates: false,
})
if err != nil {
return err
}

err = htmlg.RenderComponents(w, gpscomponent.UpdatesHeader{
RPs: mockComponentRPs,
CheckingUpdates: true,
})
if err != nil {
return err
}

err = htmlg.RenderComponents(w,
mockComponentRPs[0],
mockComponentRPs[1],
mockComponentRPs[2],
)
if err != nil {
return err
}

err = htmlg.RenderComponents(w, gpscomponent.InstalledUpdates)
if err != nil {
return err
}

err = htmlg.RenderComponents(w, mockComponentRPs[3])
if err != nil {
return err
}

_, err = io.WriteString(w, `</div></div>`)
if err != nil {
return err
}

_, err = io.WriteString(w, `</body></html>`)
return err
}

var mockComponentRPs = []*gpscomponent.RepoPresentation{
{
RepoRoot: "github.com/gopherjs/gopherjs",
ImportPathPattern: "github.com/gopherjs/gopherjs/...",
LocalRevision: "",
RemoteRevision: "",
HomeURL: "https://github.com/gopherjs/gopherjs",
ImageURL: "https://avatars.githubusercontent.com/u/6654647?v=3",
Changes: []gpscomponent.Change{
{
Message: "improved reflect support for blocking functions",
URL: "https://github.com/gopherjs/gopherjs/commit/87bf7e405aa3df6df0dcbb9385713f997408d7b9",
Comments: gpscomponent.Comments{
Count: 0,
URL: "",
},
},
{
Message: "small cleanup",
URL: "https://github.com/gopherjs/gopherjs/commit/77a838f965881a888416bae38f790f76bb1f64bd",
Comments: gpscomponent.Comments{
Count: 1,
URL: "https://www.example.com/",
},
},
{
Message: "replaced js.This and js.Arguments by js.MakeFunc",
URL: "https://github.com/gopherjs/gopherjs/commit/29dd054a0753760fe6e826ded0982a1bf69f702a",
Comments: gpscomponent.Comments{
Count: 0,
URL: "",
},
},
},
Error: "",
UpdateState: gpscomponent.Available,
UpdateSupported: true,
},
{
RepoRoot: "golang.org/x/image",
ImportPathPattern: "golang.org/x/image/...",
LocalRevision: "",
RemoteRevision: "",
HomeURL: "http://golang.org/x/image",
ImageURL: "https://avatars.githubusercontent.com/u/4314092?v=3",
Changes: []gpscomponent.Change{
{
Message: "draw: generate code paths for image.Gray sources.",
URL: "https://github.com/golang/image/commit/f510ad81a1256ee96a2870647b74fa144a30c249",
Comments: gpscomponent.Comments{
Count: 0,
URL: "",
},
},
},
Error: "",
UpdateState: gpscomponent.Updating,
UpdateSupported: true,
},
{
RepoRoot: "unknown.com/package",
ImportPathPattern: "unknown.com/package/...",
LocalRevision: "abcdef0123456789000000000000000000000000",
RemoteRevision: "d34db33f01010101010101010101010101010101",
HomeURL: "https://unknown.com/package",
ImageURL: "https://github.com/images/gravatars/gravatar-user-420.png",
Changes: nil,
Error: "",
UpdateState: gpscomponent.Available,
UpdateSupported: true,
},
{
RepoRoot: "golang.org/x/image",
ImportPathPattern: "golang.org/x/image/...",
LocalRevision: "",
RemoteRevision: "",
HomeURL: "http://golang.org/x/image",
ImageURL: "https://avatars.githubusercontent.com/u/4314092?v=3",
Changes: []gpscomponent.Change{
{
Message: "draw: generate code paths for image.Gray sources.",
URL: "https://github.com/golang/image/commit/f510ad81a1256ee96a2870647b74fa144a30c249",
Comments: gpscomponent.Comments{
Count: 0,
URL: "",
},
},
},
Error: "",
UpdateState: gpscomponent.Updated,
UpdateSupported: true,
},
}

var mockRepoPresentations = []workspace.RepoPresentation{
var mockWorkspaceRPs = []workspace.RepoPresentation{
{
Repo: &gps.Repo{
Root: (string)("github.com/gopherjs/gopherjs"),
Expand Down Expand Up @@ -129,7 +299,7 @@ var mockRepoPresentations = []workspace.RepoPresentation{
},

{
Updated: true,
UpdateState: workspace.Updated,

Repo: &gps.Repo{
Root: (string)("github.com/influxdb/influxdb"),
Expand Down
52 changes: 52 additions & 0 deletions cmd/Go-Package-Store/errorhandler.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package main

import (
"fmt"
"log"
"net/http"
"os"

"github.com/shurcooL/httperror"
)

// errorHandler factors error handling out of the HTTP handler.
type errorHandler func(w http.ResponseWriter, req *http.Request) error

func (h errorHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
err := h(w, req)
if err == nil {
// Do nothing.
return
}
if err, ok := httperror.IsMethod(err); ok {
httperror.HandleMethod(w, err)
return
}
if err, ok := httperror.IsRedirect(err); ok {
http.Redirect(w, req, err.URL, http.StatusSeeOther)
return
}
if err, ok := httperror.IsBadRequest(err); ok {
httperror.HandleBadRequest(w, err)
return
}
if err, ok := httperror.IsHTTP(err); ok {
code := err.Code
error := fmt.Sprintf("%d %s\n\n%v", code, http.StatusText(code), err)
http.Error(w, error, code)
return
}
if os.IsNotExist(err) {
log.Println(err)
http.Error(w, "404 Not Found\n\n"+err.Error(), http.StatusNotFound)
return
}
if os.IsPermission(err) {
log.Println(err)
http.Error(w, "403 Forbidden\n\n"+err.Error(), http.StatusUnauthorized)
return
}

log.Println(err)
http.Error(w, "500 Internal Server Error\n\n"+err.Error(), http.StatusInternalServerError)
}
Loading

0 comments on commit f0ae285

Please sign in to comment.