@@ -6,14 +6,14 @@ import (
	"fmt"
	"os"
	"sort"
	"strings"
	"time"
	"unicode"
	"dmitri.shuralyov.com/changes"
	"github.com/andygrunwald/go-gerrit"
	"github.com/shurcooL/issues"
	"github.com/shurcooL/users"
)
// NewService creates a Gerrit-backed issues.Service using given Gerrit client.
// client must be non-nil.
@@ -243,48 +243,126 @@ func (s service) GetDiff(ctx context.Context, _ string, id uint64, opt *changes.
		}
		return []byte(diff), nil
	}
}
func (s service) ListComments(ctx context.Context, _ string, id uint64, opt *changes.ListCommentsOptions) ([]issues.Comment, error) {
func (s service) ListTimeline(ctx context.Context, _ string, id uint64, opt *changes.ListTimelineOptions) ([]interface{}, error) {
	// TODO: Pagination. Respect opt.Start and opt.Length, if given.
	change, _, err := s.cl.Changes.GetChangeDetail(fmt.Sprint(id), nil)
	if err != nil {
		return nil, err
	}
	var comments []issues.Comment
	var timeline []interface{}
	{
		timeline = append(timeline, changes.Comment{
			ID:        0,
			User:      s.gerritUser(change.Owner),
			CreatedAt: time.Time(change.Created),
			Body:      "", // THINK: Include commit message or no?
			Editable:  false,
		})
	}
	for idx, message := range change.Messages {
		if strings.HasPrefix(message.Tag, "autogenerated:") {
			continue
		}
		comments = append(comments, issues.Comment{
		label, body, ok := parseMessage(message.Message)
		if !ok {
			continue
		}
		switch label {
		case "Code-Review+2":
			timeline = append(timeline, changes.TimelineItem{
				Actor:     s.gerritUser(message.Author),
				CreatedAt: time.Time(message.Date),
				Payload:   changes.ApprovedEvent{},
			})
		case "Code-Review-2":
			timeline = append(timeline, changes.TimelineItem{
				Actor:     s.gerritUser(message.Author),
				CreatedAt: time.Time(message.Date),
				Payload:   changes.ChangesRequestedEvent{},
			})
		}
		if body == "" {
			continue
		}
		timeline = append(timeline, changes.Comment{
			ID:        uint64(idx), // TODO: message.ID is not uint64; e.g., "bfba753d015916303152305cee7152ea7a112fe0".
			User:      s.gerritUser(message.Author),
			CreatedAt: time.Time(message.Date),
			Body:      message.Message,
			Body:      body,
			Editable:  false,
		})
	}
	return comments, nil
	return timeline, nil
}
func (s service) ListEvents(ctx context.Context, _ string, id uint64, opt *changes.ListCommentsOptions) ([]issues.Event, error) {
	// TODO.
	return nil, nil
func parseMessage(m string) (label string, body string, ok bool) {
	// "Patch Set ".
	if !strings.HasPrefix(m, "Patch Set ") {
		return "", "", false
	}
	m = m[len("Patch Set "):]
	// "123".
	i := strings.IndexFunc(m, func(c rune) bool { return !unicode.IsNumber(c) })
	if i == -1 {
		return "", "", false
	}
	m = m[i:]
	// ":".
	if len(m) < 1 || m[0] != ':' {
		return "", "", false
	}
	m = m[1:]
	switch i = strings.IndexByte(m, '\n'); i {
	case -1:
		label = m
	default:
		label = m[:i]
		body = m[i+1:]
	}
	if label != "" {
		// " ".
		if len(label) < 1 || label[0] != ' ' {
			return "", "", false
		}
		label = label[1:]
	}
	if body != "" {
		// "\n".
		if len(body) < 1 || body[0] != '\n' {
			return "", "", false
		}
		body = body[1:]
	}
	return label, body, true
}
func (s service) gerritUser(user gerrit.AccountInfo) users.User {
	var avatarURL string
	for _, avatar := range user.Avatars {
		if avatar.Height == 100 {
			avatarURL = avatar.URL
		}
	}
	return users.User{
		UserSpec: users.UserSpec{
			ID:     uint64(user.AccountID),
			Domain: s.domain,
		},
		Login: user.Name, //user.Username, // TODO.
		Name:  user.Name,
		//Email:     user.Email,
		AvatarURL: fmt.Sprintf("https://%s/accounts/%d/avatar?s=96", s.domain, user.AccountID),
		AvatarURL: avatarURL,
	}
}
func project(repo string) string {
	if i := strings.IndexByte(repo, '/'); i != -1 {