package display import ( "fmt" "strconv" "strings" "time" "github.com/dustin/go-humanize" "github.com/gdamore/tcell" "github.com/makeworld-the-better-one/amfora/config" "github.com/spf13/viper" "gitlab.com/tslocum/cview" ) // This file contains code for the popups / modals used in the display. // The bookmark modal is in bookmarks.go var infoModal = cview.NewModal(). AddButtons([]string{"Ok"}) var errorModal = cview.NewModal(). AddButtons([]string{"Ok"}) var inputModal = cview.NewModal() var inputCh = make(chan string) var inputModalText string // The current text of the input field in the modal var yesNoModal = cview.NewModal(). AddButtons([]string{"Yes", "No"}) // Channel to receive yesNo answer on var yesNoCh = make(chan bool) func modalInit() { tabPages.AddPage("info", infoModal, false, false). AddPage("error", errorModal, false, false). AddPage("input", inputModal, false, false). AddPage("yesno", yesNoModal, false, false). AddPage("bkmk", bkmkModal, false, false). AddPage("dlChoice", dlChoiceModal, false, false). AddPage("dl", dlModal, false, false) // Color setup if viper.GetBool("a-general.color") { infoModal.SetBackgroundColor(config.GetColor("info_modal_bg")). SetButtonBackgroundColor(config.GetColor("btn_bg")). SetButtonTextColor(config.GetColor("btn_text")). SetTextColor(config.GetColor("info_modal_text")) infoModal.GetFrame(). SetBorderColor(config.GetColor("info_modal_text")). SetTitleColor(config.GetColor("info_modal_text")) errorModal.SetBackgroundColor(config.GetColor("error_modal_bg")). SetButtonBackgroundColor(config.GetColor("btn_bg")). SetButtonTextColor(config.GetColor("btn_text")). SetTextColor(config.GetColor("error_modal_text")) errorModal.GetFrame(). SetBorderColor(config.GetColor("error_modal_text")). SetTitleColor(config.GetColor("error_modal_text")) inputModal.SetBackgroundColor(config.GetColor("input_modal_bg")). SetButtonBackgroundColor(config.GetColor("btn_bg")). SetButtonTextColor(config.GetColor("btn_text")). SetTextColor(config.GetColor("input_modal_text")) inputModal.GetFrame(). SetBorderColor(config.GetColor("input_modal_text")). SetTitleColor(config.GetColor("input_modal_text")) inputModal.GetForm(). SetFieldBackgroundColor(config.GetColor("input_modal_field_bg")). SetFieldTextColor(config.GetColor("input_modal_field_text")) yesNoModal.SetButtonBackgroundColor(config.GetColor("btn_bg")). SetButtonTextColor(config.GetColor("btn_text")) } else { infoModal.SetBackgroundColor(tcell.ColorBlack). SetButtonBackgroundColor(tcell.ColorWhite). SetButtonTextColor(tcell.ColorBlack). SetTextColor(tcell.ColorWhite) infoModal.GetFrame(). SetBorderColor(tcell.ColorWhite). SetTitleColor(tcell.ColorWhite) errorModal.SetBackgroundColor(tcell.ColorBlack). SetButtonBackgroundColor(tcell.ColorWhite). SetButtonTextColor(tcell.ColorBlack). SetTextColor(tcell.ColorWhite) errorModal.GetFrame(). SetBorderColor(tcell.ColorWhite). SetTitleColor(tcell.ColorWhite) inputModal.SetBackgroundColor(tcell.ColorBlack). SetButtonBackgroundColor(tcell.ColorWhite). SetButtonTextColor(tcell.ColorBlack). SetTextColor(tcell.ColorWhite) inputModal.GetFrame(). SetBorderColor(tcell.ColorWhite). SetTitleColor(tcell.ColorWhite) inputModal.GetForm(). SetFieldBackgroundColor(tcell.ColorWhite). SetFieldTextColor(tcell.ColorBlack) // YesNo background color is changed in funcs yesNoModal.SetButtonBackgroundColor(tcell.ColorWhite). SetButtonTextColor(tcell.ColorBlack) } // Modal functions that can't be added up above, because they return the wrong type infoModal.SetBorder(true) infoModal.GetFrame(). SetTitleAlign(cview.AlignCenter). SetTitle(" Info ") infoModal.SetDoneFunc(func(buttonIndex int, buttonLabel string) { tabPages.SwitchToPage(strconv.Itoa(curTab)) App.SetFocus(tabs[curTab].view) App.Draw() }) errorModal.SetBorder(true) errorModal.GetFrame().SetTitleAlign(cview.AlignCenter) errorModal.SetDoneFunc(func(buttonIndex int, buttonLabel string) { tabPages.SwitchToPage(strconv.Itoa(curTab)) App.SetFocus(tabs[curTab].view) App.Draw() }) inputModal.SetBorder(true) inputModal.GetFrame(). SetTitleAlign(cview.AlignCenter). SetTitle(" Input ") inputModal.SetDoneFunc(func(buttonIndex int, buttonLabel string) { if buttonLabel == "Send" { inputCh <- inputModalText return } // Empty string indicates no input inputCh <- "" }) yesNoModal.SetBorder(true) yesNoModal.GetFrame().SetTitleAlign(cview.AlignCenter) yesNoModal.SetDoneFunc(func(buttonIndex int, buttonLabel string) { if buttonLabel == "Yes" { yesNoCh <- true return } yesNoCh <- false }) bkmkInit() dlInit() } // Error displays an error on the screen in a modal. func Error(title, text string) { // Capitalize and add period if necessary - because most errors don't do that text = strings.ToUpper(string([]rune(text)[0])) + text[1:] if !strings.HasSuffix(text, ".") && !strings.HasSuffix(text, "!") && !strings.HasSuffix(text, "?") { text += "." } // Add spaces to title for aesthetic reasons title = " " + strings.TrimSpace(title) + " " errorModal.GetFrame().SetTitle(title) errorModal.SetText(text) tabPages.ShowPage("error") tabPages.SendToFront("error") App.SetFocus(errorModal) App.Draw() } // Info displays some info on the screen in a modal. func Info(s string) { infoModal.SetText(s) tabPages.ShowPage("info") tabPages.SendToFront("info") App.SetFocus(infoModal) App.Draw() } // Input pulls up a modal that asks for input, and returns the user's input. // It returns an bool indicating if the user chose to send input or not. func Input(prompt string) (string, bool) { // Remove elements and re-add them - to clear input text and keep input in focus inputModal.ClearButtons() inputModal.GetForm().Clear(false) inputModal.AddButtons([]string{"Send", "Cancel"}) inputModalText = "" inputModal.GetForm().AddInputField("", "", 0, nil, func(text string) { // Store for use later inputModalText = text }) inputModal.SetText(prompt + " ") tabPages.ShowPage("input") tabPages.SendToFront("input") App.SetFocus(inputModal) App.Draw() resp := <-inputCh tabPages.SwitchToPage(strconv.Itoa(curTab)) App.SetFocus(tabs[curTab].view) App.Draw() if resp == "" { return "", false } return resp, true } // YesNo displays a modal asking a yes-or-no question. func YesNo(prompt string) bool { if viper.GetBool("a-general.color") { yesNoModal. SetBackgroundColor(config.GetColor("yesno_modal_bg")). SetTextColor(config.GetColor("yesno_modal_text")) yesNoModal.GetFrame(). SetBorderColor(config.GetColor("yesno_modal_text")). SetTitleColor(config.GetColor("yesno_modal_text")) } else { yesNoModal. SetBackgroundColor(tcell.ColorBlack). SetTextColor(tcell.ColorWhite) yesNoModal.GetFrame(). SetBorderColor(tcell.ColorWhite). SetTitleColor(tcell.ColorWhite) } yesNoModal.GetFrame().SetTitle("") yesNoModal.SetText(prompt) tabPages.ShowPage("yesno") tabPages.SendToFront("yesno") App.SetFocus(yesNoModal) App.Draw() resp := <-yesNoCh tabPages.SwitchToPage(strconv.Itoa(curTab)) App.SetFocus(tabs[curTab].view) App.Draw() return resp } // Tofu displays the TOFU warning modal. // It returns a bool indicating whether the user wants to continue. func Tofu(host string, expiry time.Time) bool { // Reuses yesNoModal, with error color if viper.GetBool("a-general.color") { yesNoModal. SetBackgroundColor(config.GetColor("tofu_modal_bg")). SetTextColor(config.GetColor("tofu_modal_text")) yesNoModal.GetFrame(). SetBorderColor(config.GetColor("tofu_modal_text")). SetTitleColor(config.GetColor("tofu_modal_text")) } else { yesNoModal. SetBackgroundColor(tcell.ColorBlack). SetTextColor(tcell.ColorWhite) yesNoModal. SetBorderColor(tcell.ColorWhite). SetTitleColor(tcell.ColorWhite) } yesNoModal.GetFrame().SetTitle(" TOFU ") yesNoModal.SetText( fmt.Sprintf("%s's certificate has changed, possibly indicating an security issue. The certificate would have expired %s. Are you sure you want to continue? ", host, humanize.Time(expiry), ), ) tabPages.ShowPage("yesno") tabPages.SendToFront("yesno") App.SetFocus(yesNoModal) App.Draw() resp := <-yesNoCh tabPages.SwitchToPage(strconv.Itoa(curTab)) App.SetFocus(tabs[curTab].view) App.Draw() return resp }