Skip to content

Commit

Permalink
feat: issue author assignee (#70)
Browse files Browse the repository at this point in the history
* feat: issue author and assignee

* chore: ui cleanup

* fix(demo): prevent stopping non-created players

* test: parse url

* test: TestSplitLabelsToLines

* test: github issue fetch
  • Loading branch information
igor-sirotin authored Jul 9, 2024
1 parent 190b6b1 commit 3cce292
Show file tree
Hide file tree
Showing 11 changed files with 646 additions and 152 deletions.
33 changes: 27 additions & 6 deletions cmd/2sp/demo/demo.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,8 @@ func (d *Demo) Stop() {

func (d *Demo) initializePlayers() error {
names := []string{"Alice", "Bob", "Charlie"}
d.players = make([]*game.Game, len(names))
d.playerSubs = make([]game.StateSubscription, len(names))
d.players = make([]*game.Game, 0, len(names))
d.playerSubs = make([]game.StateSubscription, 0, len(names))

wg := sync.WaitGroup{}
errChan := make(chan error, len(names))
Expand All @@ -78,8 +78,8 @@ func (d *Demo) initializePlayers() error {
errChan <- errors.Wrap(err, "failed to create player")
return
}
d.players[i] = player
d.playerSubs[i] = player.SubscribeToStateChanges()
d.players = append(d.players, player)
d.playerSubs = append(d.playerSubs, player.SubscribeToStateChanges())
}(i, name)
}

Expand All @@ -99,14 +99,19 @@ func (d *Demo) Routine() {
defer d.Stop()

d.logger.Info("started")
time.Sleep(2 * time.Second) // Wait for the program to start

err := d.waitForGameInitialized()
if err != nil {
d.logger.Error("game wasn't initialized", zap.Error(err))
return
}

// Create new room
d.sendShortcut(commands.DefaultKeyMap.NewRoom)
d.logger.Info("room created")

// Add players
err := d.initializePlayers()
err = d.initializePlayers()
if err != nil {
d.logger.Error("failed to initialize players", zap.Error(err))
return
Expand Down Expand Up @@ -338,3 +343,19 @@ func (d *Demo) waitForIssueDealt(sub game.StateSubscription, issueID protocol.Is
return state.ActiveIssue == issueID
})
}

func (d *Demo) waitForGameInitialized() error {
timeout := time.After(10 * time.Second)
for {
select {
case <-d.ctx.Done():
return errors.New("context done")
case <-timeout:
return errors.New("timeout waiting for game to be initialized")
case <-time.After(100 * time.Millisecond):
if d.dealer.Initialized() {
return nil
}
}
}
}
17 changes: 8 additions & 9 deletions internal/view/DESIGN.md
Original file line number Diff line number Diff line change
@@ -1,20 +1,19 @@
package view

# Full example

## Room view

```shell

● Waku: 8 peer(s)
Room: W1Rka5Dz8GVGzgy1iq6gS5puS1x3JroeW4ZDTfBZkCfm (dealer)
● Waku: 8 peer(s) | ● Room: W1Rka5Dz8GVGzgy1iq6gS5puS1x3JroeW4ZDTfBZkCfm (dealer)

https://github.com/golang/go/issues/19412
proposal: spec: add sum types / discriminated unions #19412

Author: @Alice [LanguageChange] [v2] [Proposal]
Assignee: @Bob [NeedsInvestigation]

Issue: https://github.com/golang/go/issues/19412
proposal: spec: add sum types / discriminated unions
[LanguageChange] [v2] [Proposal] [NeedsInvestigation]

╭───────┬───────────┬─────────┬───────┬──────╮
│ Alice │ Bob (You) │ Charlie │ David │ Erin │ Recommended: 8
│ Alice │ Bob (You) │ Charlie │ David │ Erin │ Recommended: 8
├───────┼───────────┼─────────┼───────┼──────┤ Acceptable: ✓
│ 8 │ 8 │ 5 │ 5 │ 8 │ > Not bad.
╰───────┴───────────┴─────────┴───────┴──────╯
Expand Down
137 changes: 137 additions & 0 deletions internal/view/components/issueview/fetch.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
package issueview

import (
"context"
"net/url"
"strconv"
"strings"
"time"

tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
"github.com/muesli/termenv"
"github.com/pkg/errors"

"github.com/six78/2-story-points-cli/internal/config"
"github.com/six78/2-story-points-cli/pkg/protocol"
)

var (
errOnlyGithubIssuesUnfurled = errors.New("only github issues can be unfurled")
errInvalidGithubIssueLink = errors.New("invalid github issue link")
errInvalidGithubIssueNumber = errors.New("invalid github issue number")
errGithubIssueFetchFailed = errors.New("failed to fetch github issue")
)

type githubIssueRequest struct {
owner string
repo string
number int
}

func parseUrl(input string) (*githubIssueRequest, error) {
u, err := url.Parse(input)
if err != nil {
return nil, nil
}
if u.Host != "github.com" {
return nil, errOnlyGithubIssuesUnfurled
}
path := strings.Split(u.Path, "/")
if len(path) != 5 {
return nil, errInvalidGithubIssueLink
}

issueNumber, err := strconv.Atoi(path[4])
if err != nil {
return nil, errInvalidGithubIssueNumber
}

return &githubIssueRequest{
owner: path[1],
repo: path[2],
number: issueNumber,
}, nil
}

func fetchIssue(client GithubIssueService, input *protocol.Issue) tea.Cmd {
return func() tea.Msg {
if input == nil {
return nil
}
request, err := parseUrl(input.TitleOrURL)
if err != nil {
return issueFetchedMessage{
url: input.TitleOrURL,
info: &issueInfo{
err: err,
},
}
}
if request == nil {
return nil
}
ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)
defer cancel()

issue, _, err := client.Get(ctx, request.owner, request.repo, request.number)
if err != nil {
return issueFetchedMessage{
url: input.TitleOrURL,
info: &issueInfo{
err: errGithubIssueFetchFailed,
},
}
}

labels := make([]labelInfo, len(issue.Labels))
for i, label := range issue.Labels {
labels[i].name = label.Name
labels[i].style = labelStyle(label.Color)
}

msg := issueFetchedMessage{
url: input.TitleOrURL,
info: &issueInfo{
err: nil,
number: issue.Number,
title: issue.Title,
labels: labels,
},
}

if issue.User != nil {
msg.info.author = issue.User.Login
}

if issue.Assignee != nil {
msg.info.assignee = issue.Assignee.Login
}

return msg
}
}

func labelStyle(input *string) lipgloss.Style {
if input == nil {
return lipgloss.NewStyle().Foreground(config.ForegroundShadeColor)
}

color := lipgloss.Color("#" + *input)
dark := colorIsDark(color)

if lipgloss.DefaultRenderer().HasDarkBackground() == dark {
return lipgloss.NewStyle().Background(color)
}

return lipgloss.NewStyle().Foreground(color)
}

func colorIsDark(color lipgloss.Color) bool {
renderer := lipgloss.DefaultRenderer()
c := renderer.ColorProfile().Color(string(color))
rgb := termenv.ConvertToRGB(c)
//_, _, lightness := rgb.Hsl()
perceivedLightness := 0.2126*rgb.R + 0.7152*rgb.G + 0.0722*rgb.B
return perceivedLightness < 0.453
}
Loading

0 comments on commit 3cce292

Please sign in to comment.