@@ 379,6 379,11 @@ func (elections *ElectionsMap) onStartElectionMessage(evt *event.Event) (success
return
}
+ if len(content.JoinIDs) == 1 {
+ warnf("there must be more than one join")
+ return
+ }
+
contents := make([]event.Content, 0, 1+len(content.JoinIDs))
contents = append(contents, createEvt.Content)
@@ 410,25 410,7 @@ func ElectionWaitTUI(kill <-chan error, client *mautrix.Client, el *election.Ele
}
defer handlePanic()
- el.RLock()
- if el.LocalVoter != nil && el.CreateEvt.Sender == el.LocalVoter.JoinEvt.Sender {
- frame.AddText("Press enter to start the election", false, tview.AlignCenter, tcell.ColorWhite)
- app.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
- if event.Key() == tcell.KeyEnter {
- frame.Clear()
- frame.AddText("Starting election...", false, tview.AlignCenter, tcell.ColorWhite)
- err = el.StartElection(client, eventStore)
- }
- return event
- })
- } else {
- frame.AddText("Waiting for election to start...", false, tview.AlignCenter, tcell.ColorWhite)
- }
- if err != nil {
- return
- }
- el.RUnlock()
- update := func() {
+ updateVoters := func() {
el.RLock()
defer el.RUnlock()
// TODO: handle users who joined after cutoff
@@ 451,53 433,98 @@ func ElectionWaitTUI(kill <-chan error, client *mautrix.Client, el *election.Ele
text = "Joined voters:\n" + text
votersTextView.SetText(text)
}
+
+ doStartElection := make(chan struct{}, 1)
+
+ startElectionInputCapture := func(event *tcell.EventKey) *tcell.EventKey {
+ if event.Key() != tcell.KeyEnter {
+ return event
+ }
+
+ // so the election isn't started more than once
+ app.SetInputCapture(nil)
+
+ frame.Clear()
+ frame.AddText("Starting election...", false, tview.AlignCenter, tcell.ColorWhite)
+
+ // actually start election in loop goroutine so text gets
+ // updated first
+ doStartElection <- struct{}{}
+
+ return event
+ }
+
+ queueUpdateText := func(txt string) {
+ app.QueueUpdateDraw(func() {
+ frame.Clear()
+ frame.AddText(txt, false, tview.AlignCenter, tcell.ColorWhite)
+ updateVoters()
+ })
+ }
+
go func() {
+ // exit screen on return
+ defer app.Stop()
defer handlePanic()
- var electionStarted bool
- for app.alive {
+ var inputCaptureSet bool
+ userIsCreator := userIsCreator(el)
+ for {
el.RLock()
- if electionStarted {
- // have we received everyone's keys?
- receivedAllKeys := true
+ electionHasStarted := el.FinalJoinIDs != nil
+ userKeysSent := el.LocalVoter.KeysID != nil
+ enoughVoters := len(el.Joins) > 1
+
+ var haveReceivedAllKeys bool
+ if electionHasStarted && userKeysSent {
+ haveReceivedAllKeys = true
for _, joinID := range *el.FinalJoinIDs {
if el.Joins[joinID].KeysID == nil {
- receivedAllKeys = false
+ haveReceivedAllKeys = false
break
}
}
- el.RUnlock()
- if receivedAllKeys {
- app.Stop()
+ }
+ el.RUnlock()
+
+ if electionHasStarted {
+ if haveReceivedAllKeys {
return
- }
- } else if el.FinalJoinIDs != nil {
- electionStarted = true
- el.RUnlock()
- if el.LocalVoter.KeysID == nil {
- app.QueueUpdateDraw(func() {
- frame.Clear()
- frame.AddText("Sending keys...", false, tview.AlignCenter, tcell.ColorWhite)
- update()
- })
+ } else if userKeysSent {
+ queueUpdateText("Waiting for everyone's keys...")
+ } else {
+ queueUpdateText("Sending keys...")
err = el.SendProvingKeys(client, eventStore)
- if err != nil {
- app.Stop()
- return
+ }
+ } else if userIsCreator {
+ select {
+ case <-doStartElection:
+ err = el.StartElection(client, eventStore)
+ default:
+ if enoughVoters {
+ if !inputCaptureSet {
+ // only set once to prevent
+ // starting the election more
+ // than once
+ inputCaptureSet = true
+ app.SetInputCapture(startElectionInputCapture)
+ }
+ queueUpdateText("Press enter to start the election")
+ } else {
+ queueUpdateText("Waiting for more voters...")
}
}
- app.QueueUpdateDraw(func() {
- frame.Clear()
- frame.AddText("Waiting for everyone's keys...", false, tview.AlignCenter, tcell.ColorWhite)
- update()
- })
} else {
- el.RUnlock()
- app.QueueUpdateDraw(update)
+ queueUpdateText("Waiting for election to start...")
+ }
+
+ if err != nil {
+ return
}
time.Sleep(1 * time.Second)
}
}()
+
app.SetRoot(frame, true)
err2 := app.Run()
if err != nil {
@@ 509,6 536,12 @@ func ElectionWaitTUI(kill <-chan error, client *mautrix.Client, el *election.Ele
return nil
}
+func userIsCreator(el *election.Election) bool {
+ el.RLock()
+ defer el.RUnlock()
+ return el.LocalVoter != nil && el.CreateEvt.Sender == el.LocalVoter.JoinEvt.Sender
+}
+
// displays a voting UI to the user and returns the encoded ballot
func VoteTUI(kill <-chan error, candidates []election.Candidate) (ballot *[][]byte, err error) {
app := newTallyardApplication(kill)