Skip to content
Open
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
41 changes: 3 additions & 38 deletions client/webserver/site/src/css/proposal.scss
Original file line number Diff line number Diff line change
@@ -1,48 +1,13 @@
.vote-icon {
color: #fff;
display: block;
}

.proposal-content {
h1, h2, h3 {
font-size: 1.3rem;
}
}

.vote-btn {
display: flex;
align-items: center;
width: 100%;
max-width: 420px;
border: none;
font-weight: 600;
cursor: pointer;
color: #fff;
}

.vote-btn .icon {
width: 20px;
height: 20px;
border-radius: 50%;
display: grid;
place-items: center;
font-size: 20px;
font-weight: 700;
}

.vote-btn .label {
flex: 1;
text-align: center;
}

.vote-btn.vote-yes {
background: #6dff4f;
}

.vote-btn.vote-no {
background: #d81e14;
}

.vote-btn.active {
border: 3px solid var(--btn-go-bg);
&.active {
outline: 3px solid var(--text-color);
}
}
40 changes: 12 additions & 28 deletions client/webserver/site/src/html/proposal.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -119,36 +119,20 @@
<form class="d-hide" id="voteForm" autocomplete="off">
<div class="form-closer"><span class="ico-cross"></span></div>
<header>[[[vote]]]</header>
<p class="text-muted mt-2">
<div class="fs15 text-center text-muted">
[[[cast_vote_prompt]]]
</p>
<div>[[[voting_power]]]: {{$votingPower}}</div>
<div class="vote-actions">
<button class="vote-btn vote-yes rounded3" type="button" data-value="yes">
<span class="icon">
<svg class="vote-icon" viewBox="0 0 24 24" width="20" height="20" fill="none"
xmlns="http://www.w3.org/2000/svg">
<path d="M5 13l4 4L19 7" stroke="currentColor" stroke-width="3" stroke-linecap="round"
stroke-linejoin="round" />
</svg>
</span>
<span class="label">[[[Yes]]]</span>
</button>

<button class="vote-btn vote-no mt-2 rounded3" type="button" data-value="no">
<span class="icon">
<svg class="vote-icon" viewBox="0 0 24 24" width="20" height="20" fill="none"
xmlns="http://www.w3.org/2000/svg">
<path d="M6 6l12 12M18 6L6 18" stroke="currentColor" stroke-width="3"
stroke-linecap="round" />
</svg>
</span>
<span class="label">[[[No]]]</span>
</button>
</div>

<button id="voteSubmit" type="button" class="mt-3">[[[submit_vote]]]</button>
<div id="voteFormError" class="text-danger text-center d-hide"></div>
<div class="d-flex justify-content-center fs15">
[[[voting_power]]]: {{$votingPower}}
</div>
<div class="d-flex justify-content-center mt-1">
<button class="vote-btn go rounded3 me-2" type="button" data-value="yes">[[[Yes]]]</button>
<button class="vote-btn danger rounded3" type="button" data-value="no">[[[No]]]</button>
</div>
<div class="flex-stretch-column">
<button id="voteSubmit" type="button" class="feature">[[[submit_vote]]]</button>
</div>
<div class="fs15 text-center d-hide text-danger text-break" id="voteFormError"></div>
</form>
</div>
</section>
Expand Down
1 change: 1 addition & 0 deletions client/webserver/site/src/js/proposal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ export default class ProposalPage extends BasePage {
Doc.show(page.voteFormError)
return
}
Doc.hide(page.viewVoteFormBtn)
this.closePopups()
this.showSuccess(intl.prep(intl.ID_VOTE_CAST_MESSAGE))
}
Expand Down
53 changes: 37 additions & 16 deletions dex/politeia/politeia.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,32 +141,45 @@ func New(ctx context.Context, politeiaURL string, dbPath string, log dex.Logger)
// have voted along with their votes, and the total yes and no votes cast by
// the wallet.
func (p *Politeia) WalletProposalVoteDetails(wallet VotingWallet, token string) (*WalletProposalVoteDetails, error) {
req := tkv1.Results{
Token: token,
}
votesResults, err := p.client.TicketVoteResults(req)
// Fetch the vote details to get the full list of eligible tickets
// from the snapshot.
voteDetails, err := p.client.TicketVoteDetails(tkv1.Details{Token: token})
if err != nil {
return nil, err
return nil, fmt.Errorf("TicketVoteDetails error: %w", err)
}
if voteDetails.Vote == nil {
return nil, fmt.Errorf("no vote details for proposal %s", token)
}

hashes := make([]*chainhash.Hash, 0, len(votesResults.Votes))
castVotes := make(map[string]string)
for _, v := range votesResults.Votes {
hash, err := chainhash.NewHashFromStr(v.Ticket)
// Build the list of all eligible ticket hashes from the vote snapshot.
eligibleHashes := make([]*chainhash.Hash, 0, len(voteDetails.Vote.EligibleTickets))
for _, ticketStr := range voteDetails.Vote.EligibleTickets {
hash, err := chainhash.NewHashFromStr(ticketStr)
if err != nil {
return nil, err
}
hashes = append(hashes, hash)
eligibleHashes = append(eligibleHashes, hash)
}

// Fetch the votes already cast to know which tickets have voted.
votesResults, err := p.client.TicketVoteResults(tkv1.Results{Token: token})
if err != nil {
return nil, fmt.Errorf("TicketVoteResults error: %w", err)
}

castVotes := make(map[string]string, len(votesResults.Votes))
for _, v := range votesResults.Votes {
castVotes[v.Ticket] = v.VoteBit
}

walletTicketHashes, addresses, err := wallet.CommittedTickets(hashes)
// Check which eligible tickets belong to this wallet.
walletTicketHashes, addresses, err := wallet.CommittedTickets(eligibleHashes)
if err != nil {
return nil, err
}

eligibleWalletTickets := make([]*Ticket, 0) // eligibleWalletTickets are wallet tickets that have not yet voted.
walletVotedTickets := make([]*Ticket, 0) // walletVotedTickets are wallet tickets that have voted.
eligibleWalletTickets := make([]*Ticket, 0)
walletVotedTickets := make([]*Ticket, 0)
for i := 0; i < len(walletTicketHashes); i++ {
ticket := &Ticket{
Hash: walletTicketHashes[i].String(),
Expand All @@ -176,17 +189,18 @@ func (p *Politeia) WalletProposalVoteDetails(wallet VotingWallet, token string)
isMine, accountNumber, err := wallet.AddressAccount(ticket.Address)
if err != nil {
if strings.Contains(err.Error(), "address not found in wallet") {
continue // address not found in wallet, skip this ticket
continue
}
return nil, err
}

// filter out tickets controlled by imported accounts or not owned by this wallet
// Filter out tickets controlled by imported accounts or not
// owned by this wallet.
if !isMine || accountNumber == udb.ImportedAddrAccount {
continue
}

// filter out wallet tickets that have voted.
// Separate into already-voted and eligible.
if _, ok := castVotes[ticket.Hash]; ok {
walletVotedTickets = append(walletVotedTickets, ticket)
continue
Expand Down Expand Up @@ -262,6 +276,13 @@ func (p *Politeia) CastVotes(wallet VotingWallet, eligibleTickets []*Ticket, bit
return fmt.Errorf("errors casting votes: %v", strings.Join(voteErrors, "; "))
}

ticketHashes := make([]string, len(eligibleTickets))
for i, t := range eligibleTickets {
ticketHashes[i] = t.Hash
}
p.log.Infof("Successfully cast %d %s vote(s) on proposal %s with tickets: %s",
len(eligibleTickets), bit, token, strings.Join(ticketHashes, ", "))

return nil
}

Expand Down
Loading