diff --git a/acn.go b/acn.go index e303ac7..65be29b 100644 --- a/acn.go +++ b/acn.go @@ -23,34 +23,29 @@ const ( connected ) -func ensureConnected(m model) (bool, tea.Msg) { - switch m.connState { +func isConnected(m model) (bool, tea.Msg) { + switch m.acnState { case offline: - return false, offlineMsg{} + return false, acnOfflineMsg{} case connecting: - return false, stillConnectingMsg{} + return false, acnConnectingMsg{} default: - return true, nil + return true, acnAlreadyOnlineMsg{} } } -func attemptShutdown(m *model) tea.Msg { - switch m.connState { - case offline: - close(m.statusBuffer) - return shutdownMsg{quit: true} - case connecting: - return stillConnectingMsg{} +func turnAcnOn(m *model) tea.Msg { + if ok, msg := isConnected(*m); !ok { + switch msg.(type) { + case acnConnectingMsg: + return msg + case acnAlreadyOnlineMsg: + return msg + } } - m.app.Shutdown() - m.acn.Close() - close(m.statusBuffer) + m.acnState = connecting - return shutdownMsg{quit: true} -} - -func acnOn(m *model) tea.Msg { mrand.Seed(int64(time.Now().Nanosecond())) port := mrand.Intn(1000) + 9600 controlPort := port + 1 @@ -58,15 +53,15 @@ func acnOn(m *model) tea.Msg { key := make([]byte, 64) _, err := rand.Read(key) if err != nil { - return errMsg{Err: fmt.Errorf("unable to generate control port password: %s", err)} + return acnErrMsg{err: fmt.Errorf("unable to generate control port password: %s", err)} } if err := os.MkdirAll(path.Join(m.userDir, "/.tor", "tor"), 0700); err != nil { - return errMsg{Err: fmt.Errorf("unable to create tor directory: %s", err)} + return acnErrMsg{err: fmt.Errorf("unable to create tor directory: %s", err)} } if err := os.MkdirAll(path.Join(m.userDir, "profiles"), 0700); err != nil { - return errMsg{Err: fmt.Errorf("unable to create profiles directory: %s", err)} + return acnErrMsg{err: fmt.Errorf("unable to create profiles directory: %s", err)} } if err := tor.NewTorrc(). @@ -75,7 +70,7 @@ func acnOn(m *model) tea.Msg { WithControlPort(controlPort). WithHashedPassword(base64.StdEncoding.EncodeToString(key)). Build(filepath.Join(m.userDir, ".tor", "tor", "torrc")); err != nil { - return errMsg{Err: fmt.Errorf("unable to initialise torrc builder: %s", err)} + return acnErrMsg{err: fmt.Errorf("unable to initialise torrc builder: %s", err)} } m.sendStatus("Initialising Tor ACN...") @@ -90,18 +85,18 @@ func acnOn(m *model) tea.Msg { }, ) if err != nil { - return errMsg{Err: fmt.Errorf("unable to bootstrap tor: %s", err)} + return acnErrMsg{err: fmt.Errorf("unable to bootstrap tor: %s", err)} } m.sendStatus("Waiting for ACN to bootstrap...") if err := acn.WaitTillBootstrapped(); err != nil { - return errMsg{Err: fmt.Errorf("unable to initialise tor: %s", err)} + return acnErrMsg{err: fmt.Errorf("unable to initialise tor: %s", err)} } settingsFile, err := settings.InitGlobalSettingsFile(m.userDir, "") if err != nil { - return errMsg{Err: fmt.Errorf("unable to initialise settings: %s", err)} + return acnErrMsg{err: fmt.Errorf("unable to initialise settings: %s", err)} } gSettings := settingsFile.ReadGlobalSettings() gSettings.ExperimentsEnabled = false @@ -111,27 +106,24 @@ func acnOn(m *model) tea.Msg { app := app.NewApp(acn, m.userDir, settingsFile) app.InstallEngineHooks(connections.DefaultEngineHooks{}) - m.sendStatus("Tor ACN is up and running, all engines go") - - return appInitialisedMsg{ + return acnOnMsg{ app: app, acn: acn, } } -func acnOff(m *model) tea.Msg { - switch m.connState { +func turnAcnOff(m *model, quitProgram bool) tea.Msg { + switch m.acnState { case offline: - return offlineMsg{} + if !quitProgram { + return acnOfflineMsg{} + } case connecting: - return stillConnectingMsg{} + return acnConnectingMsg{} } m.app.Shutdown() m.acn.Close() - return shutdownMsg{ - quit: false, - status: "ACN successfully turned off", - } + return acnOffMsg{quitProgram: quitProgram} } diff --git a/input.go b/input.go index a557cd5..2e52b47 100644 --- a/input.go +++ b/input.go @@ -87,10 +87,10 @@ func handleCommand(cmd, hiddenInput string) tea.Msg { return cmdMsg{output: strings.Split(cmdHelp, "\n")} case isQuitCmd(cmds): - return shutdownMsg{quit: true} + return turnAcnOffMsg{quitProgram: true} case isStartCmd(cmds): - return startMsg{} + return showGettingStartedGuideMsg{} case isProfileCreateCmd(cmds): if len(cmds) != 5 { @@ -120,13 +120,13 @@ func handleCommand(cmd, hiddenInput string) tea.Msg { return profileInfoMsg{} case isAcnOnCmd(cmds): - return acnOnMsg{} + return turnAcnOnMsg{} case isAcnOffCmd(cmds): - return acnOffMsg{} + return turnAcnOffMsg{} case isClearCmd(cmds): - return clearMsg{} + return clearScreenMsg{} default: return cmdMsg{output: []string{"unknown command"}} diff --git a/message.go b/message.go index 36202eb..b6aac6c 100644 --- a/message.go +++ b/message.go @@ -5,34 +5,50 @@ import ( "git.openprivacy.ca/openprivacy/connectivity" ) -type sendStatusMsg struct{ lines []string } +// --------------------------------------------------------------------------- +// ACN messages +// --------------------------------------------------------------------------- +type turnAcnOnMsg struct{} -type renderStatusMsg struct{ line string } - -type errMsg struct{ Err error } - -func (e errMsg) Error() string { - return e.Err.Error() -} - -type appInitialisedMsg struct { +type acnOnMsg struct { app app.Application acn connectivity.ACN } -type cmdMsg struct{ output []string } - -type offlineMsg struct{} - -type startMsg struct{} - -type stillConnectingMsg struct{} - -type shutdownMsg struct { - quit bool - status string +type turnAcnOffMsg struct { + quitProgram bool } +type acnOffMsg struct { + quitProgram bool +} + +type acnAlreadyOnlineMsg struct{} + +type acnOfflineMsg struct{} + +type acnConnectingMsg struct{} + +type acnErrMsg struct { + err error +} + +func (e acnErrMsg) Error() string { + return e.err.Error() +} + +// --------------------------------------------------------------------------- +// help messages +// --------------------------------------------------------------------------- +type showGettingStartedGuideMsg struct{} + +// --------------------------------------------------------------------------- +// profile messages +// --------------------------------------------------------------------------- +type profileInfoMsg struct{} + +type startProfileQueuePollMsg struct{ onion string } + type createProfileMsg struct { name string password string @@ -42,12 +58,13 @@ type profileUnlockMsg struct { password string } -type acnOffMsg struct{} +// --------------------------------------------------------------------------- +// TODO: categorize messages below +// --------------------------------------------------------------------------- +type sendStatusMsg struct{ lines []string } -type acnOnMsg struct{} +type renderStatusMsg struct{ line string } -type clearMsg struct{} +type cmdMsg struct{ output []string } -type profileInfoMsg struct{} - -type startProfileQueuePollMsg struct{ onion string } +type clearScreenMsg struct{} diff --git a/model.go b/model.go index 6a039c4..665ef54 100644 --- a/model.go +++ b/model.go @@ -58,7 +58,7 @@ type model struct { username string userDir string - connState int + acnState int menuState int menuBar []string @@ -99,7 +99,7 @@ func newModel(username, homeDir string, debug bool) model { username: username, userDir: path.Join(homeDir, "/.cairde/"), - connState: offline, + acnState: offline, menuBar: []string{"STATUS"}, menuState: 0, @@ -224,7 +224,7 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { switch msg.String() { case "ctrl+c": cmds = append(cmds, func() tea.Msg { - return attemptShutdown(&m) + return turnAcnOff(&m, true) }) case "enter": @@ -249,7 +249,9 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { } case "ctrl+l": - cmds = append(cmds, func() tea.Msg { return clearMsg{} }) + cmds = append(cmds, func() tea.Msg { + return clearScreenMsg{} + }) default: hidePasswordInput(&m) @@ -259,34 +261,59 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { m.input.Reset() cmds = append(cmds, m.sendStatusCmd(msg.output...)) - case offlineMsg: + case turnAcnOffMsg: + m.input.Reset() + cmds = append(cmds, func() tea.Msg { + return turnAcnOff(&m, msg.quitProgram) + }) + + case acnOffMsg: + if msg.quitProgram { + return m, tea.Quit + } + cmds = append(cmds, m.sendStatusCmd("ACN successfully turned off")) + m.acnState = offline + + case turnAcnOnMsg: + m.input.Reset() + cmds = append(cmds, func() tea.Msg { + return turnAcnOn(&m) + }) + + case acnOnMsg: + m.app = msg.app + m.acn = msg.acn + m.app.ActivateEngines(true, true, true) + m.acnState = connected + cmds = append(cmds, m.sendStatusCmd("ACN is up and running, all engines go")) + + case acnAlreadyOnlineMsg: + m.input.Reset() + cmds = append(cmds, m.sendStatusCmd("ACN is already online")) + + case acnOfflineMsg: m.input.Reset() cmds = append(cmds, m.sendStatusCmd("ACN is currently offline")) - case stillConnectingMsg: + case acnConnectingMsg: m.input.Reset() - cmds = append(cmds, m.sendStatusCmd("Still initialising ACN, please hold")) + cmds = append(cmds, m.sendStatusCmd("ACN still initialising, please hold")) - case startMsg: + case acnErrMsg: + m.acnState = offline + cmds = append(cmds, m.sendStatusCmd(msg.Error())) + + case showGettingStartedGuideMsg: cmds = append(cmds, m.sendStatusCmd( strings.Split(gettingStartedMessage, "\n")..., )) m.input.Reset() - case shutdownMsg: - m.input.Reset() - if msg.quit { - return m, tea.Quit - } - if msg.status != "" { - cmds = append(cmds, m.sendStatusCmd(msg.status)) - } - case createProfileMsg: m.hiddenInput = "" m.input.Reset() - if isConnected, msg := ensureConnected(m); !isConnected { + if ok, msg := isConnected(m); !ok { cmds = append(cmds, func() tea.Msg { return msg }) break } @@ -315,7 +342,7 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { m.hiddenInput = "" m.input.Reset() - if isConnected, msg := ensureConnected(m); !isConnected { + if ok, msg := isConnected(m); !ok { cmds = append(cmds, func() tea.Msg { return msg }) break } @@ -339,7 +366,7 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { case profileInfoMsg: m.input.Reset() - if isConnected, msg := ensureConnected(m); !isConnected { + if ok, msg := isConnected(m); !ok { cmds = append(cmds, func() tea.Msg { return msg }) break } @@ -373,32 +400,11 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { m.statusViewport.GotoBottom() cmds = append(cmds, m.receiveStatusCmd) - case clearMsg: + case clearScreenMsg: m.input.Reset() m.statusViewportLines = []string{} m.statusViewport.SetContent("") m.statusViewport.GotoTop() - - case acnOffMsg: - m.input.Reset() - cmds = append(cmds, func() tea.Msg { - return acnOff(&m) - }) - - case acnOnMsg: - m.input.Reset() - cmds = append(cmds, func() tea.Msg { - return acnOn(&m) - }) - - case appInitialisedMsg: - m.app = msg.app - m.acn = msg.acn - m.connState = connected - m.app.ActivateEngines(true, true, true) - - case errMsg: - cmds = append(cmds, m.sendStatusCmd(msg.Error())) } return m, tea.Batch(cmds...)