2020-06-18 16:54:48 -04:00
package display
import (
2020-06-21 20:37:27 -04:00
"fmt"
2020-06-18 16:54:48 -04:00
"strings"
2020-07-10 18:59:51 -04:00
"time"
2020-06-18 16:54:48 -04:00
2021-04-07 12:56:32 -04:00
"code.rocketnine.space/tslocum/cview"
2020-08-22 09:03:13 -04:00
humanize "github.com/dustin/go-humanize"
2021-02-17 14:17:13 -05:00
"github.com/gdamore/tcell/v2"
2020-07-28 16:58:32 -04:00
"github.com/makeworld-the-better-one/amfora/config"
2020-06-24 11:37:32 -04:00
"github.com/spf13/viper"
2020-06-18 16:54:48 -04:00
)
2020-06-23 20:07:25 -04:00
// This file contains code for the popups / modals used in the display.
// The bookmark modal is in bookmarks.go
2020-06-18 16:54:48 -04:00
2021-02-17 14:17:13 -05:00
var infoModal = cview . NewModal ( )
2020-06-18 16:54:48 -04:00
2021-02-17 14:17:13 -05:00
var errorModal = cview . NewModal ( )
2020-06-18 16:54:48 -04:00
2020-07-28 16:58:32 -04:00
var inputModal = cview . NewModal ( )
2020-06-18 16:54:48 -04:00
var inputCh = make ( chan string )
var inputModalText string // The current text of the input field in the modal
2021-02-17 14:17:13 -05:00
var yesNoModal = cview . NewModal ( )
2020-06-18 16:54:48 -04:00
2020-06-18 17:23:54 -04:00
// Channel to receive yesNo answer on
2020-06-18 16:54:48 -04:00
var yesNoCh = make ( chan bool )
func modalInit ( ) {
2021-02-17 14:17:13 -05:00
infoModal . AddButtons ( [ ] string { "Ok" } )
errorModal . AddButtons ( [ ] string { "Ok" } )
yesNoModal . AddButtons ( [ ] string { "Yes" , "No" } )
panels . AddPanel ( "info" , infoModal , false , false )
panels . AddPanel ( "error" , errorModal , false , false )
panels . AddPanel ( "input" , inputModal , false , false )
panels . AddPanel ( "yesno" , yesNoModal , false , false )
2020-06-23 20:07:25 -04:00
2020-06-24 11:37:32 -04:00
// Color setup
if viper . GetBool ( "a-general.color" ) {
2021-02-17 14:17:13 -05:00
m := infoModal
m . SetBackgroundColor ( config . GetColor ( "info_modal_bg" ) )
m . SetButtonBackgroundColor ( config . GetColor ( "btn_bg" ) )
m . SetButtonTextColor ( config . GetColor ( "btn_text" ) )
m . SetTextColor ( config . GetColor ( "info_modal_text" ) )
form := m . GetForm ( )
form . SetButtonBackgroundColorFocused ( config . GetColor ( "btn_text" ) )
form . SetButtonTextColorFocused ( config . GetColor ( "btn_bg" ) )
frame := m . GetFrame ( )
frame . SetBorderColor ( config . GetColor ( "info_modal_text" ) )
frame . SetTitleColor ( config . GetColor ( "info_modal_text" ) )
m = errorModal
m . SetBackgroundColor ( config . GetColor ( "error_modal_bg" ) )
m . SetButtonBackgroundColor ( config . GetColor ( "btn_bg" ) )
m . SetButtonTextColor ( config . GetColor ( "btn_text" ) )
m . SetTextColor ( config . GetColor ( "error_modal_text" ) )
form = m . GetForm ( )
form . SetButtonBackgroundColorFocused ( config . GetColor ( "btn_text" ) )
form . SetButtonTextColorFocused ( config . GetColor ( "btn_bg" ) )
frame = errorModal . GetFrame ( )
frame . SetBorderColor ( config . GetColor ( "error_modal_text" ) )
frame . SetTitleColor ( config . GetColor ( "error_modal_text" ) )
m = inputModal
m . SetBackgroundColor ( config . GetColor ( "input_modal_bg" ) )
m . SetButtonBackgroundColor ( config . GetColor ( "btn_bg" ) )
m . SetButtonTextColor ( config . GetColor ( "btn_text" ) )
m . SetTextColor ( config . GetColor ( "input_modal_text" ) )
frame = inputModal . GetFrame ( )
frame . SetBorderColor ( config . GetColor ( "input_modal_text" ) )
frame . SetTitleColor ( config . GetColor ( "input_modal_text" ) )
form = inputModal . GetForm ( )
form . SetFieldBackgroundColor ( config . GetColor ( "input_modal_field_bg" ) )
form . SetFieldTextColor ( config . GetColor ( "input_modal_field_text" ) )
form . SetButtonBackgroundColorFocused ( config . GetColor ( "btn_text" ) )
form . SetButtonTextColorFocused ( config . GetColor ( "btn_bg" ) )
m = yesNoModal
m . SetButtonBackgroundColor ( config . GetColor ( "btn_bg" ) )
m . SetButtonTextColor ( config . GetColor ( "btn_text" ) )
form = m . GetForm ( )
form . SetButtonBackgroundColorFocused ( config . GetColor ( "btn_text" ) )
form . SetButtonTextColorFocused ( config . GetColor ( "btn_bg" ) )
2020-06-24 11:37:32 -04:00
} else {
2021-02-17 14:17:13 -05:00
m := infoModal
m . SetBackgroundColor ( tcell . ColorBlack )
m . SetButtonBackgroundColor ( tcell . ColorWhite )
m . SetButtonTextColor ( tcell . ColorBlack )
m . SetTextColor ( tcell . ColorWhite )
form := m . GetForm ( )
form . SetButtonBackgroundColorFocused ( tcell . ColorBlack )
form . SetButtonTextColorFocused ( tcell . ColorWhite )
frame := infoModal . GetFrame ( )
frame . SetBorderColor ( tcell . ColorWhite )
frame . SetTitleColor ( tcell . ColorWhite )
m = errorModal
m . SetBackgroundColor ( tcell . ColorBlack )
m . SetButtonBackgroundColor ( tcell . ColorWhite )
m . SetButtonTextColor ( tcell . ColorBlack )
m . SetTextColor ( tcell . ColorWhite )
form = m . GetForm ( )
form . SetButtonBackgroundColorFocused ( tcell . ColorBlack )
form . SetButtonTextColorFocused ( tcell . ColorWhite )
frame = errorModal . GetFrame ( )
frame . SetBorderColor ( tcell . ColorWhite )
frame . SetTitleColor ( tcell . ColorWhite )
m = inputModal
m . SetBackgroundColor ( tcell . ColorBlack )
m . SetButtonBackgroundColor ( tcell . ColorWhite )
m . SetButtonTextColor ( tcell . ColorBlack )
m . SetTextColor ( tcell . ColorWhite )
frame = inputModal . GetFrame ( )
frame . SetBorderColor ( tcell . ColorWhite )
frame . SetTitleColor ( tcell . ColorWhite )
form = inputModal . GetForm ( )
form . SetFieldBackgroundColor ( tcell . ColorWhite )
form . SetFieldTextColor ( tcell . ColorBlack )
form . SetButtonBackgroundColorFocused ( tcell . ColorBlack )
form . SetButtonTextColorFocused ( tcell . ColorWhite )
2020-06-24 11:37:32 -04:00
// YesNo background color is changed in funcs
2021-02-17 14:17:13 -05:00
m = yesNoModal
m . SetButtonBackgroundColor ( tcell . ColorWhite )
m . SetButtonTextColor ( tcell . ColorBlack )
form = m . GetForm ( )
form . SetButtonBackgroundColorFocused ( tcell . ColorBlack )
form . SetButtonTextColorFocused ( tcell . ColorWhite )
2020-06-24 11:37:32 -04:00
}
2020-06-18 16:54:48 -04:00
// Modal functions that can't be added up above, because they return the wrong type
2020-06-23 20:07:25 -04:00
2020-06-18 16:54:48 -04:00
infoModal . SetBorder ( true )
2021-02-17 14:17:13 -05:00
frame := infoModal . GetFrame ( )
frame . SetTitleAlign ( cview . AlignCenter )
frame . SetTitle ( " Info " )
2020-06-18 16:54:48 -04:00
infoModal . SetDoneFunc ( func ( buttonIndex int , buttonLabel string ) {
2021-02-17 14:17:13 -05:00
panels . HidePanel ( "info" )
2020-07-28 18:09:36 -04:00
App . SetFocus ( tabs [ curTab ] . view )
App . Draw ( )
2020-06-18 16:54:48 -04:00
} )
errorModal . SetBorder ( true )
2020-07-28 16:58:32 -04:00
errorModal . GetFrame ( ) . SetTitleAlign ( cview . AlignCenter )
2020-06-18 16:54:48 -04:00
errorModal . SetDoneFunc ( func ( buttonIndex int , buttonLabel string ) {
2021-02-17 14:17:13 -05:00
panels . HidePanel ( "error" )
2020-07-28 18:09:36 -04:00
App . SetFocus ( tabs [ curTab ] . view )
App . Draw ( )
2020-06-18 16:54:48 -04:00
} )
inputModal . SetBorder ( true )
2021-02-17 14:17:13 -05:00
frame = inputModal . GetFrame ( )
frame . SetTitleAlign ( cview . AlignCenter )
frame . SetTitle ( " Input " )
2020-06-18 16:54:48 -04:00
inputModal . SetDoneFunc ( func ( buttonIndex int , buttonLabel string ) {
if buttonLabel == "Send" {
inputCh <- inputModalText
return
}
// Empty string indicates no input
inputCh <- ""
} )
yesNoModal . SetBorder ( true )
2020-07-28 16:58:32 -04:00
yesNoModal . GetFrame ( ) . SetTitleAlign ( cview . AlignCenter )
2020-06-18 16:54:48 -04:00
yesNoModal . SetDoneFunc ( func ( buttonIndex int , buttonLabel string ) {
if buttonLabel == "Yes" {
yesNoCh <- true
return
}
yesNoCh <- false
} )
2020-06-23 20:07:25 -04:00
bkmkInit ( )
2020-07-10 14:37:18 -04:00
dlInit ( )
2020-06-18 16:54:48 -04:00
}
// Error displays an error on the screen in a modal.
func Error ( title , text string ) {
2020-08-22 09:03:13 -04:00
if text == "" {
text = "No additional information."
} else {
text = strings . ToUpper ( string ( [ ] rune ( text ) [ 0 ] ) ) + text [ 1 : ]
if ! strings . HasSuffix ( text , "." ) && ! strings . HasSuffix ( text , "!" ) && ! strings . HasSuffix ( text , "?" ) {
text += "."
}
2020-06-18 16:54:48 -04:00
}
// Add spaces to title for aesthetic reasons
title = " " + strings . TrimSpace ( title ) + " "
2020-06-19 14:05:05 -04:00
errorModal . GetFrame ( ) . SetTitle ( title )
2020-06-18 16:54:48 -04:00
errorModal . SetText ( text )
2021-02-17 14:17:13 -05:00
panels . ShowPanel ( "error" )
panels . SendToFront ( "error" )
2020-06-18 16:54:48 -04:00
App . SetFocus ( errorModal )
App . Draw ( )
}
// Info displays some info on the screen in a modal.
func Info ( s string ) {
infoModal . SetText ( s )
2021-02-17 14:17:13 -05:00
panels . ShowPanel ( "info" )
panels . SendToFront ( "info" )
2020-06-18 16:54:48 -04:00
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.
2021-02-17 14:48:03 -05:00
func Input ( prompt string , sensitive bool ) ( string , bool ) {
2020-06-29 13:30:20 -04:00
// 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" } )
2020-06-18 16:54:48 -04:00
inputModalText = ""
2021-02-17 14:48:03 -05:00
if sensitive {
// TODO use bullet characters if user wants it once bug is fixed - see NOTES.md
inputModal . GetForm ( ) . AddPasswordField ( "" , "" , 0 , '*' ,
func ( text string ) {
// Store for use later
inputModalText = text
} )
} else {
inputModal . GetForm ( ) . AddInputField ( "" , "" , 0 , nil ,
func ( text string ) {
inputModalText = text
} )
}
2020-06-18 16:54:48 -04:00
2020-07-28 16:58:32 -04:00
inputModal . SetText ( prompt + " " )
2021-02-17 14:17:13 -05:00
panels . ShowPanel ( "input" )
panels . SendToFront ( "input" )
2020-06-18 16:54:48 -04:00
App . SetFocus ( inputModal )
App . Draw ( )
resp := <- inputCh
2020-07-28 18:09:36 -04:00
2021-02-17 14:17:13 -05:00
panels . HidePanel ( "input" )
2020-07-28 18:09:36 -04:00
App . SetFocus ( tabs [ curTab ] . view )
App . Draw ( )
2020-06-18 16:54:48 -04:00
if resp == "" {
return "" , false
}
return resp , true
}
// YesNo displays a modal asking a yes-or-no question.
func YesNo ( prompt string ) bool {
2020-06-24 11:37:32 -04:00
if viper . GetBool ( "a-general.color" ) {
2021-02-17 14:17:13 -05:00
m := yesNoModal
m . SetBackgroundColor ( config . GetColor ( "yesno_modal_bg" ) )
m . SetTextColor ( config . GetColor ( "yesno_modal_text" ) )
frame := yesNoModal . GetFrame ( )
frame . SetBorderColor ( config . GetColor ( "yesno_modal_text" ) )
frame . SetTitleColor ( config . GetColor ( "yesno_modal_text" ) )
2020-06-24 11:37:32 -04:00
} else {
2021-02-17 14:17:13 -05:00
m := yesNoModal
m . SetBackgroundColor ( tcell . ColorBlack )
m . SetTextColor ( tcell . ColorWhite )
frame := yesNoModal . GetFrame ( )
frame . SetBorderColor ( tcell . ColorWhite )
frame . SetTitleColor ( tcell . ColorWhite )
2020-06-24 11:37:32 -04:00
}
2020-07-10 18:59:51 -04:00
yesNoModal . GetFrame ( ) . SetTitle ( "" )
2020-06-24 11:37:32 -04:00
yesNoModal . SetText ( prompt )
2021-02-17 14:17:13 -05:00
panels . ShowPanel ( "yesno" )
panels . SendToFront ( "yesno" )
2020-06-21 20:37:27 -04:00
App . SetFocus ( yesNoModal )
App . Draw ( )
resp := <- yesNoCh
2021-02-17 14:17:13 -05:00
panels . HidePanel ( "yesno" )
2020-07-28 18:09:36 -04:00
App . SetFocus ( tabs [ curTab ] . view )
App . Draw ( )
2020-06-21 20:37:27 -04:00
return resp
}
// Tofu displays the TOFU warning modal.
// It returns a bool indicating whether the user wants to continue.
2020-07-10 18:59:51 -04:00
func Tofu ( host string , expiry time . Time ) bool {
2020-07-28 16:58:32 -04:00
// Reuses yesNoModal, with error color
2020-06-21 20:37:27 -04:00
2021-02-17 14:17:13 -05:00
m := yesNoModal
frame := yesNoModal . GetFrame ( )
2020-06-24 11:37:32 -04:00
if viper . GetBool ( "a-general.color" ) {
2021-02-17 14:17:13 -05:00
m . SetBackgroundColor ( config . GetColor ( "tofu_modal_bg" ) )
m . SetTextColor ( config . GetColor ( "tofu_modal_text" ) )
frame . SetBorderColor ( config . GetColor ( "tofu_modal_text" ) )
frame . SetTitleColor ( config . GetColor ( "tofu_modal_text" ) )
2020-06-24 11:37:32 -04:00
} else {
2021-02-17 14:17:13 -05:00
m . SetBackgroundColor ( tcell . ColorBlack )
m . SetTextColor ( tcell . ColorWhite )
m . SetBorderColor ( tcell . ColorWhite )
m . SetTitleColor ( tcell . ColorWhite )
2020-06-24 11:37:32 -04:00
}
2021-02-17 14:17:13 -05:00
frame . SetTitle ( " TOFU " )
m . SetText (
2020-08-27 11:47:57 -04:00
//nolint:lll
2020-07-10 18:59:51 -04:00
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 ) ,
) ,
2020-06-21 20:37:27 -04:00
)
2021-02-17 14:17:13 -05:00
panels . ShowPanel ( "yesno" )
panels . SendToFront ( "yesno" )
2020-06-18 16:54:48 -04:00
App . SetFocus ( yesNoModal )
App . Draw ( )
resp := <- yesNoCh
2021-02-17 14:17:13 -05:00
panels . HidePanel ( "yesno" )
2020-07-28 18:09:36 -04:00
App . SetFocus ( tabs [ curTab ] . view )
App . Draw ( )
2020-06-18 16:54:48 -04:00
return resp
}