Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: HintView #56

Merged
merged 4 commits into from
Jun 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 10 additions & 10 deletions internal/view/DESIGN.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,18 @@ package view

```shell

Room: 238mvM91TbC9UJdUqKwtWFb
● Waku: 8 peer(s)
Room: W1Rka5Dz8GVGzgy1iq6gS5puS1x3JroeW4ZDTfBZkCfm (dealer)

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

Issue: https://github.com/golang/go/issues/19412
Title: Implement room encryption

╭───────┬─────┬─────────┬────────┬─────────╮
│ Alice │ Bob │ Charlie │ Didukh │ Sirotin │
├───────┼─────┼─────────┼────────┼─────────┤
│ 1 │ 3 │ 8 │ 13 │ 3 │
╰───────┴─────┴─────────┴────────┴─────────╯

Your vote:
│ Alice │ Bob │ Charlie │ Didukh │ Sirotin │ Recommended: 3
├───────┼─────┼─────────┼────────┼─────────┤ Acceptable: x - too big votes variety
│ 1 │ 3 │ 8 │ 13 │ 3 │ What to do: Listen to Alice and Didukh arguments
╰───────┴─────┴─────────┴────────┴─────────╯
╭───╮
╭───╮ ╭───╮ │ 3 │ ╭───╮ ╭───╮ ╭────╮ ╭────╮ ╭────╮
│ 1 │ │ 2 │ ╰───╯ │ 5 │ │ 8 │ │ 13 │ │ 21 │ │ 34 │
Expand Down
78 changes: 78 additions & 0 deletions internal/view/components/hintview/model.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package hintview

import (
"fmt"

tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"

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

var (
headerStyle = lipgloss.NewStyle() // .Foreground(lipgloss.Color("#FAFAFA"))
acceptableStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("#00FF00"))
unacceptableStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("#FF0000"))
textStyle = lipgloss.NewStyle() // .Foreground(lipgloss.Color("#FAFAFA"))
MentionStyle = textStyle.Copy().Italic(true).Foreground(config.UserColor)
)

type Model struct {
hint *protocol.Hint
}

func New() Model {
return Model{
hint: nil,
}
}

func (m Model) Init() tea.Cmd {
return nil
}

func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) {
switch msg := msg.(type) {
case messages.GameStateMessage:
if msg.State == nil || !msg.State.VotesRevealed {
m.hint = nil
break
}

issue := msg.State.Issues.Get(msg.State.ActiveIssue)
if issue != nil {
m.hint = issue.Hint
}
}

return m, nil
}

func (m Model) View() string {
if m.hint == nil {
return ""
}

verdictStyle := unacceptableStyle
verdictText := "x"
if m.hint.Acceptable {
verdictStyle = acceptableStyle
verdictText = "✓"
}

rejectionReason := ""
if !m.hint.Acceptable {
rejectionReason = fmt.Sprintf(" (%s)", textStyle.Render(m.hint.RejectReason))
}

return lipgloss.JoinVertical(lipgloss.Top,
"",
headerStyle.Render("Recommended:")+""+voteview.Render(m.hint.Value),
headerStyle.Render("Acceptable:")+" "+verdictStyle.Render(verdictText)+rejectionReason,
headerStyle.Render("What to do:")+" "+textStyle.Render(m.hint.Advice),
"",
)
}
111 changes: 111 additions & 0 deletions internal/view/components/hintview/model_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
package hintview

import (
"fmt"
"strings"
"testing"

"github.com/brianvoe/gofakeit/v6"
"github.com/stretchr/testify/require"

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

func TestInit(t *testing.T) {
model := New()
cmd := model.Init()
require.Nil(t, cmd)
require.Nil(t, model.hint)
}

func TestUpdateNilState(t *testing.T) {
model := New()
_ = model.Init()
model, cmd := model.Update(messages.GameStateMessage{State: nil})
require.Nil(t, cmd)
require.Nil(t, model.hint)
require.Empty(t, model.View())
}

func TestUpdateVotesNotRevealed(t *testing.T) {
model := New()
_ = model.Init()
model, cmd := model.Update(messages.GameStateMessage{
State: &protocol.State{
VotesRevealed: false,
},
})
require.Nil(t, cmd)
require.Nil(t, model.hint)
require.Empty(t, model.View())
}

func TestUpdateAcceptableVote(t *testing.T) {
model := New()

cmd := model.Init()
require.Nil(t, cmd)
require.Nil(t, model.hint)

// Test acceptable vote

testCases := []struct {
name string
acceptable bool
}{
{
name: "acceptable vote",
acceptable: true,
},
{
name: "non-acceptable vote",
acceptable: false,
},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {

issue := protocol.Issue{
ID: protocol.IssueID(gofakeit.UUID()),
Hint: &protocol.Hint{
Acceptable: tc.acceptable,
Value: protocol.VoteValue(gofakeit.LetterN(5)),
RejectReason: gofakeit.LetterN(10),
},
}
model, cmd = model.Update(messages.GameStateMessage{
State: &protocol.State{
Issues: protocol.IssuesList{&issue},
ActiveIssue: issue.ID,
VotesRevealed: true,
},
})
require.Nil(t, cmd)
require.NotNil(t, model.hint)
require.Equal(t, *issue.Hint, *model.hint)

expectedVerdict := "✓"
if !tc.acceptable {
expectedVerdict = "x" + fmt.Sprintf(" (%s)", issue.Hint.RejectReason)
}

expectedLines := []string{
"",
"Recommended: " + string(issue.Hint.Value),
"Acceptable: " + expectedVerdict,
"What to do:",
"",
}

lines := strings.Split(model.View(), "\n")
require.Len(t, lines, len(expectedLines))

for i, line := range lines {
trimmedLine := strings.Trim(line, " ")
require.Equal(t, expectedLines[i], trimmedLine)
}
})
}
}
8 changes: 7 additions & 1 deletion internal/view/components/voteview/voteview.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package voteview

import (
"strconv"

"github.com/charmbracelet/bubbles/spinner"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"

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

var (
Expand Down Expand Up @@ -141,3 +143,7 @@ func VoteStyle(vote protocol.VoteValue) *lipgloss.Style {
}
return &LightVoteStyle
}

func Render(vote protocol.VoteValue) string {
return VoteStyle(vote).Render(string(vote))
}
15 changes: 11 additions & 4 deletions internal/view/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,24 @@ package view

import (
"fmt"
"net/url"
"strings"
"time"

"github.com/charmbracelet/bubbles/key"
"github.com/charmbracelet/bubbles/spinner"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
"github.com/pkg/errors"
"go.uber.org/zap"

"github.com/six78/2-story-points-cli/internal/config"
"github.com/six78/2-story-points-cli/internal/transport"
"github.com/six78/2-story-points-cli/internal/view/commands"
"github.com/six78/2-story-points-cli/internal/view/components/deckview"
"github.com/six78/2-story-points-cli/internal/view/components/errorview"
"github.com/six78/2-story-points-cli/internal/view/components/eventhandler"
"github.com/six78/2-story-points-cli/internal/view/components/hintview"
"github.com/six78/2-story-points-cli/internal/view/components/issuesview"
"github.com/six78/2-story-points-cli/internal/view/components/issueview"
"github.com/six78/2-story-points-cli/internal/view/components/playersview"
Expand All @@ -24,10 +31,6 @@ import (
"github.com/six78/2-story-points-cli/internal/view/update"
"github.com/six78/2-story-points-cli/pkg/game"
"github.com/six78/2-story-points-cli/pkg/protocol"
"go.uber.org/zap"
"net/url"
"strings"
"time"
)

type model struct {
Expand All @@ -47,6 +50,7 @@ type model struct {
roomViewState states.RoomView
errorView errorview.Model
playersView playersview.Model
hintView hintview.Model
shortcutsView shortcutsview.Model
wakuStatusView wakustatusview.Model
deckView deckview.Model
Expand Down Expand Up @@ -85,6 +89,7 @@ func initialModel(game *game.Game, transport transport.Service) model {
spinner: createSpinner(),
errorView: errorview.New(),
playersView: playersview.New(),
hintView: hintview.New(),
shortcutsView: shortcutsview.New(),
wakuStatusView: wakustatusview.New(),
deckView: deckView,
Expand All @@ -109,6 +114,7 @@ func (m model) Init() tea.Cmd {
m.spinner.Tick,
m.errorView.Init(),
m.playersView.Init(),
m.hintView.Init(),
m.shortcutsView.Init(),
m.wakuStatusView.Init(),
m.deckView.Init(),
Expand Down Expand Up @@ -290,6 +296,7 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
m.spinner, cmds.SpinnerCommand = m.spinner.Update(msg)
m.errorView = m.errorView.Update(msg)
m.playersView, cmds.PlayersCommand = m.playersView.Update(msg)
m.hintView, _ = m.hintView.Update(msg)
m.shortcutsView = m.shortcutsView.Update(msg, m.roomViewState)
m.wakuStatusView = m.wakuStatusView.Update(msg)
m.deckView = m.deckView.Update(msg)
Expand Down
6 changes: 4 additions & 2 deletions internal/view/render.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@ package view

import (
"fmt"
"strings"

"github.com/charmbracelet/lipgloss"

"github.com/six78/2-story-points-cli/internal/config"
"github.com/six78/2-story-points-cli/internal/view/states"
"strings"
)

/*
Expand Down Expand Up @@ -93,7 +95,7 @@ func (m model) renderRoomCurrentIssueView() string {
return lipgloss.JoinVertical(lipgloss.Top,
m.issueView.View(),
"",
m.playersView.View(),
lipgloss.JoinHorizontal(lipgloss.Left, m.playersView.View(), " ", m.hintView.View()),
m.deckView.View(),
)
}
Expand Down
5 changes: 2 additions & 3 deletions pkg/game/hints.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (
"sort"

"github.com/pkg/errors"
"golang.org/x/exp/slices"

"github.com/six78/2-story-points-cli/pkg/protocol"
)
Expand Down Expand Up @@ -50,7 +49,7 @@ func GetResultHint(deck protocol.Deck, issueVotes protocol.IssueVotes) (*protoco

// Build the hint based on the measures
hint := &protocol.Hint{
Hint: medianValue,
Value: medianValue,
Advice: "",
Acceptable: true,
}
Expand All @@ -71,7 +70,7 @@ func GetResultHint(deck protocol.Deck, issueVotes protocol.IssueVotes) (*protoco
func getVotesAsDeckIndexes(issueVotes protocol.IssueVotes, deck protocol.Deck) ([]int, error) {
indexes := make([]int, 0, len(issueVotes))
for _, vote := range issueVotes {
index := slices.Index(deck, vote.Value)
index := deck.Index(vote.Value)
if index < 0 {
return nil, ErrVoteNotFoundInDeck
}
Expand Down
Loading
Loading