dmitri.shuralyov.com/app/changes/...

Start ability to view individual commits.
dmitshur committed 7 years ago commit 0adff4e74816eb3c353370f09245d8107e920110
Collapse all
_data/change-files.html.tmpl
@@ -8,10 +8,30 @@

		<h1>{{.Change.Title}} <span class="gray">#{{.Change.ID}}</span></h1>
		<div id="change-state-badge" style="margin-bottom: 20px;">{{render (changeStateBadge .Change)}}</div>
		{{.Tabnav "Files"}}

{{define "CommitMessage"}}
<div class="list-entry list-entry-border commit-message">
	<header class="list-entry-header">
		<div style="display: flex;">
			<pre style="flex-grow: 1;"><strong>{{.Subject}}</strong>{{with .Body}}

{{.}}{{end}}</pre>
			<span>Button</span>
		</div>
	</header>
	<div class="list-entry-body">
		<span style="display: inline-block; vertical-align: bottom; margin-right: 5px;">{{.Avatar}}</span>{{/*
		*/}}<span style="display: inline-block;">{{.User}} committed {{.Time}}</span>
		<span style="float: right;">
			<span>commit <code>{{.CommitHash}}</code></span>
		</span>
	</div>
</div>
{{end}}

{{define "FileDiff"}}
<div class="list-entry list-entry-border">
	<header class="list-entry-header">{{.Title}}</header>
	<div class="list-entry-body">
		<pre class="highlight-diff">{{.Diff}}</pre>
_data/style.css
@@ -27,15 +27,17 @@ div.list-entry-border {
	border: 1px solid #ddd;
	border-radius: 4px;
}
header.list-entry-header {
	font-size: 13px;
	background-color: #f8f8f8;
	padding: 10px;
	border-radius: 4px 4px 0 0;
	border-bottom: 1px solid #eee;
}
div:not(.commit-message) header.list-entry-header {
	background-color: #f8f8f8;
}
.hash-selected div.list-entry-border {
	border: 1px solid #8ca2d9;
}
.hash-selected header.list-entry-header {
	background-color: #dbe5ff;
@@ -55,10 +57,36 @@ div.list-entry-body {
div.multilist-entry:not(:first-of-type) {
	border: 0px solid #ddd;
	border-top-width: 1px;
}

div.commit-message.list-entry-border {
	background-color: #ecf3ff;
}
.commit-message header.list-entry-header {
	font-size: 14px;
}
.commit-message div.list-entry-body {
	background-color: #fff;
}
.commit-message pre {
	font-family: Go;
	font-size: 14px;
	tab-size: 4;
	margin: 0;
}
.commit-message div.list-entry-body {
	font-size: 13px;
	line-height: 24px;
	padding: 6px;
}

code {
	font-family: "Go Mono";
	font-size: 12px;
}

/* Needed in issuesapp only because of something in parent containers... */
.markdown-body {
	word-break: break-word;
}

commits.go
@@ -60,11 +60,11 @@ func (c Commit) Render() []*html.Node {
		title := htmlg.Div(
			&html.Node{
				Type: html.ElementNode, Data: atom.A.String(),
				Attr: []html.Attribute{
					{Key: atom.Class.String(), Val: "black"},
					{Key: atom.Href.String(), Val: "commit/" + c.SHA},
					{Key: atom.Href.String(), Val: "files/" + c.SHA},
				},
				FirstChild: htmlg.Strong(commitSubject),
			},
		)
		if commitBody != "" {
main.go
@@ -163,11 +163,16 @@ func (h *handler) ServeHTTP(w http.ResponseWriter, req *http.Request) error {
	case len(elems) == 2 && elems[1] == "commits":
		return h.ChangeCommitsHandler(w, req, changeID)

	// "/{changeID}/files".
	case len(elems) == 2 && elems[1] == "files":
		return h.ChangeFilesHandler(w, req, changeID)
		return h.ChangeFilesHandler(w, req, changeID, "")

	// "/{changeID}/files/{commitID}".
	case len(elems) == 3 && elems[1] == "files":
		commitID := elems[2]
		return h.ChangeFilesHandler(w, req, changeID, commitID)

	default:
		return httperror.HTTP{Code: http.StatusNotFound, Err: errors.New("no route")}
	}
}
@@ -336,11 +341,11 @@ func (h *handler) ChangeCommitsHandler(w http.ResponseWriter, req *http.Request,
	}
	state.Change, err = h.is.Get(req.Context(), state.RepoSpec, state.IssueID)
	if err != nil {
		return err
	}
	cs, err := h.is.ListCommits(req.Context(), state.RepoSpec, state.IssueID)
	cs, err := h.is.ListCommits(req.Context(), state.RepoSpec, state.IssueID, nil)
	if err != nil {
		return err
	}
	w.Header().Set("Content-Type", "text/html; charset=utf-8")
	err = h.static.ExecuteTemplate(w, "change-commits.html.tmpl", &state)
@@ -357,11 +362,11 @@ func (h *handler) ChangeCommitsHandler(w http.ResponseWriter, req *http.Request,
	}
	_, err = io.WriteString(w, `</body></html>`)
	return err
}

func (h *handler) ChangeFilesHandler(w http.ResponseWriter, req *http.Request, changeID uint64) error {
func (h *handler) ChangeFilesHandler(w http.ResponseWriter, req *http.Request, changeID uint64, commitID string) error {
	if req.Method != http.MethodGet {
		return httperror.Method{Allowed: []string{http.MethodGet}}
	}
	state, err := h.state(req, changeID)
	if err != nil {
@@ -369,11 +374,32 @@ func (h *handler) ChangeFilesHandler(w http.ResponseWriter, req *http.Request, c
	}
	state.Change, err = h.is.Get(req.Context(), state.RepoSpec, state.IssueID)
	if err != nil {
		return err
	}
	rawDiff, err := h.is.GetDiff(req.Context(), state.RepoSpec, state.IssueID)
	var (
		opt    *changes.ListCommitsOptions
		commit commitMessage
	)
	if commitID != "" {
		opt = &changes.ListCommitsOptions{
			Commit: commitID,
		}
		cs, err := h.is.ListCommits(req.Context(), state.RepoSpec, state.IssueID, opt)
		if err != nil {
			return err
		}
		subject, body := splitCommitMessage(cs[0].Message)
		commit = commitMessage{
			CommitHash: cs[0].SHA,
			Subject:    subject,
			Body:       body,
			Author:     cs[0].Author,
			AuthorTime: cs[0].AuthorTime,
		}
	}
	rawDiff, err := h.is.GetDiff(req.Context(), state.RepoSpec, state.IssueID, opt)
	if err != nil {
		return err
	}
	fileDiffs, err := diff.ParseMultiFileDiff(rawDiff)
	if err != nil {
@@ -382,10 +408,16 @@ func (h *handler) ChangeFilesHandler(w http.ResponseWriter, req *http.Request, c
	w.Header().Set("Content-Type", "text/html; charset=utf-8")
	err = h.static.ExecuteTemplate(w, "change-files.html.tmpl", &state)
	if err != nil {
		return err
	}
	if commitID != "" {
		err = h.static.ExecuteTemplate(w, "CommitMessage", commit)
		if err != nil {
			return err
		}
	}
	for _, f := range fileDiffs {
		err = h.static.ExecuteTemplate(w, "FileDiff", fileDiff{FileDiff: f})
		if err != nil {
			return err
		}
xxx.go
@@ -4,13 +4,16 @@ import (
	"bytes"
	"fmt"
	"html/template"
	"sort"
	"strings"
	"time"

	"github.com/shurcooL/highlight_diff"
	"github.com/shurcooL/htmlg"
	issuescomponent "github.com/shurcooL/issuesapp/component"
	"github.com/shurcooL/users"
	"github.com/sourcegraph/annotate"
	"golang.org/x/net/html"
	"golang.org/x/net/html/atom"
	"sourcegraph.com/sourcegraph/go-diff/diff"
)
@@ -84,10 +87,31 @@ func (it iconText) Render() []*html.Node {
	})
	text := htmlg.Text(it.Text)
	return []*html.Node{icon, text}
}

// commitMessage ...
type commitMessage struct {
	CommitHash string
	Subject    string
	Body       string
	Author     users.User
	AuthorTime time.Time
}

func (c commitMessage) Avatar() template.HTML {
	return template.HTML(htmlg.RenderComponentsString(issuescomponent.Avatar{User: c.Author, Size: 24}))
}

func (c commitMessage) User() template.HTML {
	return template.HTML(htmlg.RenderComponentsString(issuescomponent.User{User: c.Author}))
}

func (c commitMessage) Time() template.HTML {
	return template.HTML(htmlg.RenderComponentsString(issuescomponent.Time{Time: c.AuthorTime}))
}

// fileDiff represents a file diff for display purposes.
type fileDiff struct {
	*diff.FileDiff
}