diff --git a/pkg/display/all_rounds_table.go b/pkg/display/all_rounds_table.go index 64f07df..5258249 100644 --- a/pkg/display/all_rounds_table.go +++ b/pkg/display/all_rounds_table.go @@ -15,6 +15,8 @@ type AllRoundsTableData struct { Validators types.ValidatorsWithInfoAndAllRoundVotes DisableEmojis bool Transpose bool + + cells [][]*tview.TableCell } func NewAllRoundsTableData(disableEmojis bool, transpose bool) *AllRoundsTableData { @@ -22,60 +24,93 @@ func NewAllRoundsTableData(disableEmojis bool, transpose bool) *AllRoundsTableDa Validators: types.ValidatorsWithInfoAndAllRoundVotes{}, DisableEmojis: disableEmojis, Transpose: transpose, + cells: [][]*tview.TableCell{}, } } func (d *AllRoundsTableData) GetCell(row, column int) *tview.TableCell { - round := column - 1 - if d.Transpose { - round = len(d.Validators.RoundsVotes) - column - } - - // Table header. - if row == 0 { - text := "validator" - if column != 0 { - text = strconv.Itoa(round) - } - - return tview. - NewTableCell(text). - SetAlign(tview.AlignCenter). - SetStyle(tcell.StyleDefault.Bold(true)) + if len(d.cells) <= row { + return nil } - // First column is always validators list. - if column == 0 { - text := d.Validators.Validators[row-1].Serialize() - cell := tview.NewTableCell(text) - return cell - } - - roundVotes := d.Validators.RoundsVotes[round] - roundVote := roundVotes[row-1] - text := roundVote.Serialize(d.DisableEmojis) - - cell := tview.NewTableCell(text) - - if roundVote.IsProposer { - cell.SetBackgroundColor(tcell.ColorForestGreen) + if len(d.cells[row]) <= column { + return nil } - return cell + return d.cells[row][column] } func (d *AllRoundsTableData) GetRowCount() int { - return len(d.Validators.Validators) + 1 + return len(d.cells) } func (d *AllRoundsTableData) GetColumnCount() int { - return len(d.Validators.RoundsVotes) + 1 + if len(d.cells) == 0 { + return 0 + } + + return len(d.cells[0]) } func (d *AllRoundsTableData) SetValidators(validators types.ValidatorsWithInfoAndAllRoundVotes) { + if d.Validators.Equals(validators) { + return + } + d.Validators = validators + d.redrawCells() } func (d *AllRoundsTableData) SetTranspose(transpose bool) { d.Transpose = transpose + d.redrawCells() +} + +func (d *AllRoundsTableData) redrawCells() { + d.cells = make([][]*tview.TableCell, len(d.Validators.Validators)+1) + + for row := 0; row < len(d.Validators.Validators)+1; row++ { + d.cells[row] = make([]*tview.TableCell, len(d.Validators.RoundsVotes)+1) + + for column := 0; column < len(d.Validators.RoundsVotes)+1; column++ { + round := column - 1 + if d.Transpose { + round = len(d.Validators.RoundsVotes) - column + } + + // Table header. + if row == 0 { + text := "validator" + if column != 0 { + text = strconv.Itoa(round) + } + + d.cells[row][column] = tview. + NewTableCell(text). + SetAlign(tview.AlignCenter). + SetStyle(tcell.StyleDefault.Bold(true)) + continue + } + + // First column is always validators list. + if column == 0 { + text := d.Validators.Validators[row-1].Serialize() + cell := tview.NewTableCell(text) + d.cells[row][column] = cell + continue + } + + roundVotes := d.Validators.RoundsVotes[round] + roundVote := roundVotes[row-1] + text := roundVote.Serialize(d.DisableEmojis) + + cell := tview.NewTableCell(text) + + if roundVote.IsProposer { + cell.SetBackgroundColor(tcell.ColorForestGreen) + } + + d.cells[row][column] = cell + } + } } diff --git a/pkg/display/last_round_table.go b/pkg/display/last_round_table.go index 1938ee5..491a6b1 100644 --- a/pkg/display/last_round_table.go +++ b/pkg/display/last_round_table.go @@ -14,6 +14,8 @@ type LastRoundTableData struct { ColumnsCount int DisableEmojis bool Transpose bool + + cells [][]*tview.TableCell } func NewLastRoundTableData(columnsCount int, disableEmojis bool, transpose bool) *LastRoundTableData { @@ -22,52 +24,82 @@ func NewLastRoundTableData(columnsCount int, disableEmojis bool, transpose bool) Validators: make(types.ValidatorsWithInfo, 0), DisableEmojis: disableEmojis, Transpose: transpose, + + cells: [][]*tview.TableCell{}, } } func (d *LastRoundTableData) SetColumnsCount(count int) { d.ColumnsCount = count + d.redrawData() } func (d *LastRoundTableData) SetTranspose(transpose bool) { d.Transpose = transpose + d.redrawData() } func (d *LastRoundTableData) GetCell(row, column int) *tview.TableCell { - index := row*d.ColumnsCount + column - - if d.Transpose { - rows := d.GetRowCount() - index = column*rows + row + if len(d.cells) <= row { + return nil } - text := "" - - if index < len(d.Validators) { - text = d.Validators[index].Serialize(d.DisableEmojis) - } - - cell := tview.NewTableCell(text) - - if index < len(d.Validators) && d.Validators[index].RoundVote.IsProposer { - cell.SetBackgroundColor(tcell.ColorForestGreen) + if len(d.cells[row]) <= column { + return nil } - return cell + return d.cells[row][column] } func (d *LastRoundTableData) GetRowCount() int { - if len(d.Validators)%d.ColumnsCount == 0 { - return len(d.Validators) / d.ColumnsCount - } - - return len(d.Validators)/d.ColumnsCount + 1 + return len(d.cells) } func (d *LastRoundTableData) GetColumnCount() int { - return d.ColumnsCount + if len(d.cells) == 0 { + return 0 + } + + return len(d.cells[0]) } func (d *LastRoundTableData) SetValidators(validators types.ValidatorsWithInfo) { d.Validators = validators + d.redrawData() +} + +func (d *LastRoundTableData) redrawData() { + rowsCount := len(d.Validators)/d.ColumnsCount + 1 + if len(d.Validators)%d.ColumnsCount == 0 { + rowsCount = len(d.Validators) / d.ColumnsCount + } + + d.cells = make([][]*tview.TableCell, rowsCount) + + for row := 0; row < rowsCount; row++ { + d.cells[row] = make([]*tview.TableCell, d.ColumnsCount) + + for column := 0; column < d.ColumnsCount; column++ { + index := row*d.ColumnsCount + column + + if d.Transpose { + rows := d.GetRowCount() + index = column*rows + row + } + + text := "" + + if index < len(d.Validators) { + text = d.Validators[index].Serialize(d.DisableEmojis) + } + + cell := tview.NewTableCell(text) + + if index < len(d.Validators) && d.Validators[index].RoundVote.IsProposer { + cell.SetBackgroundColor(tcell.ColorForestGreen) + } + + d.cells[row][column] = cell + } + } } diff --git a/pkg/display/wrapper.go b/pkg/display/wrapper.go index 58fddf1..715a8da 100644 --- a/pkg/display/wrapper.go +++ b/pkg/display/wrapper.go @@ -76,7 +76,7 @@ func NewWrapper( SetBorders(false). SetSelectable(false, false). SetContent(allRoundsTableData). - SetFixed(0, 1) + SetFixed(1, 1) consensusInfoTextView := tview.NewTextView(). SetDynamicColors(true). diff --git a/pkg/types/validator.go b/pkg/types/validator.go index 6944064..12a0940 100644 --- a/pkg/types/validator.go +++ b/pkg/types/validator.go @@ -23,6 +23,25 @@ type RoundVote struct { IsProposer bool } +func (v RoundVote) Equals(other RoundVote) bool { + if v.Address != other.Address { + return false + } + + if v.Prevote != other.Prevote { + return false + } + + if v.Precommit != other.Precommit { + return false + } + + if v.IsProposer != other.IsProposer { + return false + } + + return false +} func (v RoundVote) Serialize(disableEmojis bool) string { return fmt.Sprintf( " %s %s", @@ -136,6 +155,46 @@ type ValidatorWithChainValidator struct { ChainValidator *ChainValidator } +func (v ValidatorWithChainValidator) Equals(other ValidatorWithChainValidator) bool { + if v.Validator.Index != other.Validator.Index { + return false + } + + if v.Validator.Address != other.Validator.Address { + return false + } + + if v.Validator.VotingPowerPercent.Cmp(other.Validator.VotingPowerPercent) != 0 { + return false + } + + if v.Validator.VotingPower.Cmp(other.Validator.VotingPower) != 0 { + return false + } + + if (v.ChainValidator == nil) != (other.ChainValidator == nil) { + return false + } + + if v.ChainValidator == nil && other.ChainValidator == nil { + return true + } + + if v.ChainValidator.Moniker != other.ChainValidator.Moniker { + return false + } + + if v.ChainValidator.Address != other.ChainValidator.Address { + return false + } + + if v.ChainValidator.AssignedAddress != other.ChainValidator.AssignedAddress { + return false + } + + return true +} + func (v ValidatorWithChainValidator) Serialize() string { name := v.Validator.Address if v.ChainValidator != nil { @@ -157,3 +216,35 @@ type ValidatorsWithInfoAndAllRoundVotes struct { Validators []ValidatorWithChainValidator RoundsVotes []RoundVotes } + +func (v ValidatorsWithInfoAndAllRoundVotes) Equals(other ValidatorsWithInfoAndAllRoundVotes) bool { + if len(v.RoundsVotes) != len(other.RoundsVotes) { + return false + } + + for index, roundsVotes := range v.RoundsVotes { + otherRoundsVotes := other.RoundsVotes[index] + if len(roundsVotes) != len(otherRoundsVotes) { + return false + } + + for innerIndex, roundVotes := range roundsVotes { + otherRoundVotes := otherRoundsVotes[innerIndex] + if roundVotes.Equals(otherRoundVotes) { + return false + } + } + } + + if len(v.Validators) != len(other.Validators) { + return false + } + + for index, validator := range v.Validators { + if !validator.Equals(other.Validators[index]) { + return false + } + } + + return true +}