@@ 5,6 5,7 @@ import (
"fmt"
"io/ioutil"
"os"
+ "runtime/debug"
"time"
"github.com/consensys/gnark-crypto/ecc/bls12-381/fr"
@@ 60,20 61,33 @@ func main() {
syncer.OnEvent(client.Store.(*matrix.TallyardStore).UpdateState)
electionsMap.SetupEventHooks(client, syncer)
+ kill := make(chan error, 1)
go func() {
+ errorf := func(format string, args ...interface{}) {
+ err := fmt.Errorf(format, args...)
+ log.Error(err)
+ kill <- err
+ }
+ defer func() {
+ if r := recover(); r != nil {
+ errorf("panic: %s\n%s", r, debug.Stack())
+ }
+ }()
res, err := client.CreateFilter(electionFilter)
if err != nil {
- log.Panic(err)
+ errorf("couldn't create filter: %s", err)
+ return
}
client.Store.SaveFilterID(client.UserID, res.FilterID)
err = client.Sync()
if err != nil {
- log.Panic(err)
+ errorf("error during sync: %s", err)
+ return
}
}()
// select a room
- room, err := ui.RoomListTUI(client.Store.(*matrix.TallyardStore), client.UserID, syncer)
+ room, err := ui.RoomListTUI(kill, client.Store.(*matrix.TallyardStore), client.UserID, syncer)
if err != nil {
log.Panic(err)
}
@@ 83,7 97,7 @@ func main() {
}
// select an election
- el, err := ui.RoomTUI(client, room, electionsMap, syncer)
+ el, err := ui.RoomTUI(kill, client, room, electionsMap, syncer)
if err != nil {
log.Panic(err)
}
@@ 93,7 107,7 @@ func main() {
}
// wait for election to start if needed
- err = ui.ElectionWaitTUI(client, el, electionsMap.EventStore)
+ err = ui.ElectionWaitTUI(kill, client, el, electionsMap.EventStore)
if err != nil {
log.Panic(err)
}
@@ 116,7 130,7 @@ func main() {
// vote if we need to (user may have voted in previous tallyard
// invocation)
if el.LocalVoter != nil && el.LocalVoter.Poly == nil {
- ballot, err := ui.VoteTUI(el.Candidates)
+ ballot, err := ui.VoteTUI(kill, el.Candidates)
if err != nil {
log.Panic(err)
}
@@ 2,6 2,7 @@ package ui
import (
"fmt"
+ "os"
"runtime/debug"
"sort"
"strconv"
@@ 22,8 23,8 @@ import (
// displays a TUI of rooms that the user may choose from. This presumes that the
// store is already properly saving rooms
-func RoomListTUI(store *matrix.TallyardStore, localUserID id.UserID, syncer mautrix.ExtensibleSyncer) (*election.Room, error) {
- app := newTallyardApplication()
+func RoomListTUI(kill <-chan error, store *matrix.TallyardStore, localUserID id.UserID, syncer mautrix.ExtensibleSyncer) (*election.Room, error) {
+ app := newTallyardApplication(kill)
defer func() {
if r := recover(); r != nil {
if app.alive {
@@ 178,8 179,8 @@ func (roomI *roomWithTitle) cmp(roomJ *roomWithTitle) bool {
}
-func RoomTUI(client *mautrix.Client, room *election.Room, electionsMap *election.ElectionsMap, syncer mautrix.ExtensibleSyncer) (el *election.Election, err error) {
- app := newTallyardApplication()
+func RoomTUI(kill <-chan error, client *mautrix.Client, room *election.Room, electionsMap *election.ElectionsMap, syncer mautrix.ExtensibleSyncer) (el *election.Election, err error) {
+ app := newTallyardApplication(kill)
defer func() {
if r := recover(); r != nil {
if app.alive {
@@ 241,9 242,9 @@ func RoomTUI(client *mautrix.Client, room *election.Room, electionsMap *election
}
// ask user if s/he wants to join election (or display
// results if it's already started)
- if !electionConfirmation(el) {
+ if !electionConfirmation(kill, el) {
// user needs to select a different election
- el, err = RoomTUI(client, room, electionsMap, syncer)
+ el, err = RoomTUI(kill, client, room, electionsMap, syncer)
} else if el.StartID == nil {
// user wants to join the election
err = el.JoinElection(client, electionsMap.EventStore)
@@ 253,7 254,7 @@ func RoomTUI(client *mautrix.Client, room *election.Room, electionsMap *election
}
// user opted to create election (i == 0)
- title, candidates := CreateElectionTUI()
+ title, candidates := CreateElectionTUI(kill)
if candidates == nil {
return
}
@@ 278,8 279,8 @@ func RoomTUI(client *mautrix.Client, room *election.Room, electionsMap *election
return
}
-func electionConfirmation(el *election.Election) (shouldJoin bool) {
- app := newTallyardApplication()
+func electionConfirmation(kill <-chan error, el *election.Election) (shouldJoin bool) {
+ app := newTallyardApplication(kill)
defer func() {
if r := recover(); r != nil {
if app.alive {
@@ 325,10 326,10 @@ func electionConfirmation(el *election.Election) (shouldJoin bool) {
return
}
-func CreateElectionTUI() (title string, candidates []election.Candidate) {
+func CreateElectionTUI(kill <-chan error) (title string, candidates []election.Candidate) {
var form *tview.Form
n := 2
- app := newTallyardApplication()
+ app := newTallyardApplication(kill)
defer func() {
if r := recover(); r != nil {
if app.alive {
@@ 383,11 384,11 @@ func CreateElectionTUI() (title string, candidates []election.Candidate) {
return title, candidates
}
-func ElectionWaitTUI(client *mautrix.Client, el *election.Election, eventStore *election.EventStore) (err error) {
+func ElectionWaitTUI(kill <-chan error, client *mautrix.Client, el *election.Election, eventStore *election.EventStore) (err error) {
votersTextView := tview.NewTextView()
frame := tview.NewFrame(votersTextView)
frame.SetTitle(el.Title).SetBorder(true)
- app := newTallyardApplication()
+ app := newTallyardApplication(kill)
defer func() {
if r := recover(); r != nil {
if app.alive {
@@ 487,8 488,8 @@ func ElectionWaitTUI(client *mautrix.Client, el *election.Election, eventStore *
}
// displays a voting UI to the user and returns the encoded ballot
-func VoteTUI(candidates []election.Candidate) (ballot *[][]byte, err error) {
- app := newTallyardApplication()
+func VoteTUI(kill <-chan error, candidates []election.Candidate) (ballot *[][]byte, err error) {
+ app := newTallyardApplication(kill)
defer func() {
if r := recover(); r != nil {
if app.alive {
@@ 552,8 553,19 @@ type tallyardApplication struct {
alive bool
}
-func newTallyardApplication() *tallyardApplication {
- return &tallyardApplication{tview.NewApplication(), true}
+func newTallyardApplication(kill <-chan error) *tallyardApplication {
+ app := &tallyardApplication{tview.NewApplication(), true}
+ go func() {
+ select {
+ case err := <-kill:
+ if !app.alive {
+ return
+ }
+ app.Stop()
+ fmt.Fprintln(os.Stderr, err)
+ }
+ }()
+ return app
}
func (t *tallyardApplication) Run() error {