2020-06-18 23:54:48 +03:00
package display
import (
2020-06-22 03:37:27 +03:00
"fmt"
2020-06-18 23:54:48 +03:00
"strings"
2020-07-11 01:59:51 +03:00
"time"
2020-06-18 23:54:48 +03:00
2021-04-07 19:56:32 +03:00
"code.rocketnine.space/tslocum/cview"
2020-08-22 16:03:13 +03:00
humanize "github.com/dustin/go-humanize"
2021-02-17 22:17:13 +03:00
"github.com/gdamore/tcell/v2"
2020-07-28 23:58:32 +03:00
"github.com/makeworld-the-better-one/amfora/config"
2020-06-24 18:37:32 +03:00
"github.com/spf13/viper"
2020-06-18 23:54:48 +03:00
)
2020-06-24 03:07:25 +03:00
// This file contains code for the popups / modals used in the display.
// The bookmark modal is in bookmarks.go
2020-06-18 23:54:48 +03:00
2021-02-17 22:17:13 +03:00
var infoModal = cview . NewModal ( )
var errorModal = cview . NewModal ( )
2020-07-28 23:58:32 +03:00
var inputModal = cview . NewModal ( )
2021-02-17 22:17:13 +03:00
var yesNoModal = cview . NewModal ( )
2020-06-18 23:54:48 +03:00
2021-12-23 01:09:47 +03:00
var inputCh = make ( chan string )
2020-06-18 23:54:48 +03:00
var yesNoCh = make ( chan bool )
2021-12-23 01:09:47 +03:00
var inputModalText string // The current text of the input field in the modal
// Internal channel used to know when a modal has been dismissed
var modalDone = make ( chan struct { } )
2020-06-18 23:54:48 +03:00
func modalInit ( ) {
2021-02-17 22:17:13 +03:00
infoModal . AddButtons ( [ ] string { "Ok" } )
errorModal . AddButtons ( [ ] string { "Ok" } )
yesNoModal . AddButtons ( [ ] string { "Yes" , "No" } )
2021-12-08 04:18:08 +03:00
panels . AddPanel ( PanelInfoModal , infoModal , false , false )
panels . AddPanel ( PanelErrorModal , errorModal , false , false )
panels . AddPanel ( PanelInputModal , inputModal , false , false )
panels . AddPanel ( PanelYesNoModal , yesNoModal , false , false )
2020-06-24 03:07:25 +03:00
2020-06-24 18:37:32 +03:00
// Color setup
if viper . GetBool ( "a-general.color" ) {
2021-02-17 22:17:13 +03: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" ) )
2021-08-10 22:03:34 +03:00
form . SetButtonTextColorFocused ( config . GetTextColor ( "btn_bg" , "btn_text" ) )
2021-02-17 22:17:13 +03:00
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" ) )
2021-08-10 22:03:34 +03:00
form . SetButtonTextColorFocused ( config . GetTextColor ( "btn_bg" , "btn_text" ) )
2021-02-17 22:17:13 +03:00
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" ) )
2021-08-10 22:03:34 +03:00
form . SetButtonTextColorFocused ( config . GetTextColor ( "btn_bg" , "btn_text" ) )
2021-02-17 22:17:13 +03:00
m = yesNoModal
m . SetButtonBackgroundColor ( config . GetColor ( "btn_bg" ) )
m . SetButtonTextColor ( config . GetColor ( "btn_text" ) )
form = m . GetForm ( )
form . SetButtonBackgroundColorFocused ( config . GetColor ( "btn_text" ) )
2021-08-10 22:03:34 +03:00
form . SetButtonTextColorFocused ( config . GetTextColor ( "btn_bg" , "btn_text" ) )
2020-06-24 18:37:32 +03:00
} else {
2021-02-17 22:17:13 +03: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 18:37:32 +03:00
// YesNo background color is changed in funcs
2021-02-17 22:17:13 +03: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 18:37:32 +03:00
}
2020-06-18 23:54:48 +03:00
// Modal functions that can't be added up above, because they return the wrong type
2020-06-24 03:07:25 +03:00
2020-06-18 23:54:48 +03:00
infoModal . SetBorder ( true )
2021-02-17 22:17:13 +03:00
frame := infoModal . GetFrame ( )
frame . SetTitleAlign ( cview . AlignCenter )
frame . SetTitle ( " Info " )
2020-06-18 23:54:48 +03:00
infoModal . SetDoneFunc ( func ( buttonIndex int , buttonLabel string ) {
2021-12-08 04:18:08 +03:00
panels . HidePanel ( PanelInfoModal )
2020-07-29 01:09:36 +03:00
App . SetFocus ( tabs [ curTab ] . view )
App . Draw ( )
2021-12-23 01:09:47 +03:00
modalDone <- struct { } { }
2020-06-18 23:54:48 +03:00
} )
errorModal . SetBorder ( true )
2020-07-28 23:58:32 +03:00
errorModal . GetFrame ( ) . SetTitleAlign ( cview . AlignCenter )
2020-06-18 23:54:48 +03:00
errorModal . SetDoneFunc ( func ( buttonIndex int , buttonLabel string ) {
2021-12-08 04:18:08 +03:00
panels . HidePanel ( PanelErrorModal )
2020-07-29 01:09:36 +03:00
App . SetFocus ( tabs [ curTab ] . view )
App . Draw ( )
2021-12-23 01:09:47 +03:00
modalDone <- struct { } { }
2020-06-18 23:54:48 +03:00
} )
inputModal . SetBorder ( true )
2021-02-17 22:17:13 +03:00
frame = inputModal . GetFrame ( )
frame . SetTitleAlign ( cview . AlignCenter )
frame . SetTitle ( " Input " )
2020-06-18 23:54:48 +03: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 23:58:32 +03:00
yesNoModal . GetFrame ( ) . SetTitleAlign ( cview . AlignCenter )
2020-06-18 23:54:48 +03:00
yesNoModal . SetDoneFunc ( func ( buttonIndex int , buttonLabel string ) {
if buttonLabel == "Yes" {
yesNoCh <- true
return
}
yesNoCh <- false
} )
2020-06-24 03:07:25 +03:00
bkmkInit ( )
2020-07-10 21:37:18 +03:00
dlInit ( )
2020-06-18 23:54:48 +03:00
}
2021-12-23 01:09:47 +03:00
// Error displays an error on the screen in a modal, and blocks until dismissed by the user.
2020-06-18 23:54:48 +03:00
func Error ( title , text string ) {
2020-08-22 16:03:13 +03: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 23:54:48 +03:00
}
// Add spaces to title for aesthetic reasons
title = " " + strings . TrimSpace ( title ) + " "
2020-06-19 21:05:05 +03:00
errorModal . GetFrame ( ) . SetTitle ( title )
2020-06-18 23:54:48 +03:00
errorModal . SetText ( text )
2021-12-08 04:18:08 +03:00
panels . ShowPanel ( PanelErrorModal )
panels . SendToFront ( PanelErrorModal )
2020-06-18 23:54:48 +03:00
App . SetFocus ( errorModal )
App . Draw ( )
2021-12-08 03:54:54 +03:00
2021-12-23 01:09:47 +03:00
<- modalDone
2020-06-18 23:54:48 +03:00
}
2021-12-23 01:09:47 +03:00
// Info displays some info on the screen in a modal, and blocks until dismissed by the user.
2020-06-18 23:54:48 +03:00
func Info ( s string ) {
infoModal . SetText ( s )
2021-12-08 04:18:08 +03:00
panels . ShowPanel ( PanelInfoModal )
panels . SendToFront ( PanelInfoModal )
2020-06-18 23:54:48 +03:00
App . SetFocus ( infoModal )
App . Draw ( )
2021-12-23 01:09:47 +03:00
<- modalDone
2020-06-18 23:54:48 +03:00
}
2021-12-23 01:09:47 +03:00
// Input pulls up a modal that asks for input, waits for that input, and returns it.
2020-06-18 23:54:48 +03:00
// It returns an bool indicating if the user chose to send input or not.
2021-02-17 22:48:03 +03:00
func Input ( prompt string , sensitive bool ) ( string , bool ) {
2020-06-29 20:30:20 +03: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 23:54:48 +03:00
inputModalText = ""
2021-02-17 22:48:03 +03: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 23:54:48 +03:00
2020-07-28 23:58:32 +03:00
inputModal . SetText ( prompt + " " )
2021-12-08 04:18:08 +03:00
panels . ShowPanel ( PanelInputModal )
panels . SendToFront ( PanelInputModal )
2020-06-18 23:54:48 +03:00
App . SetFocus ( inputModal )
App . Draw ( )
resp := <- inputCh
2020-07-29 01:09:36 +03:00
2021-12-08 04:18:08 +03:00
panels . HidePanel ( PanelInputModal )
2020-07-29 01:09:36 +03:00
App . SetFocus ( tabs [ curTab ] . view )
App . Draw ( )
2020-06-18 23:54:48 +03:00
if resp == "" {
return "" , false
}
return resp , true
}
2021-12-23 01:09:47 +03:00
// YesNo displays a modal asking a yes-or-no question, waits for an answer, then returns it as a bool.
2020-06-18 23:54:48 +03:00
func YesNo ( prompt string ) bool {
2020-06-24 18:37:32 +03:00
if viper . GetBool ( "a-general.color" ) {
2021-02-17 22:17:13 +03: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 18:37:32 +03:00
} else {
2021-02-17 22:17:13 +03: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 18:37:32 +03:00
}
2020-07-11 01:59:51 +03:00
yesNoModal . GetFrame ( ) . SetTitle ( "" )
2020-06-24 18:37:32 +03:00
yesNoModal . SetText ( prompt )
2021-12-08 04:18:08 +03:00
panels . ShowPanel ( PanelYesNoModal )
panels . SendToFront ( PanelYesNoModal )
2020-06-22 03:37:27 +03:00
App . SetFocus ( yesNoModal )
App . Draw ( )
resp := <- yesNoCh
2021-12-08 04:18:08 +03:00
panels . HidePanel ( PanelYesNoModal )
2020-07-29 01:09:36 +03:00
App . SetFocus ( tabs [ curTab ] . view )
App . Draw ( )
2020-06-22 03:37:27 +03:00
return resp
}
// Tofu displays the TOFU warning modal.
2021-12-23 01:09:47 +03:00
// It blocks then returns a bool indicating whether the user wants to continue.
2020-07-11 01:59:51 +03:00
func Tofu ( host string , expiry time . Time ) bool {
2020-07-28 23:58:32 +03:00
// Reuses yesNoModal, with error color
2020-06-22 03:37:27 +03:00
2021-02-17 22:17:13 +03:00
m := yesNoModal
frame := yesNoModal . GetFrame ( )
2020-06-24 18:37:32 +03:00
if viper . GetBool ( "a-general.color" ) {
2021-02-17 22:17:13 +03: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 18:37:32 +03:00
} else {
2021-02-17 22:17:13 +03:00
m . SetBackgroundColor ( tcell . ColorBlack )
m . SetTextColor ( tcell . ColorWhite )
m . SetBorderColor ( tcell . ColorWhite )
m . SetTitleColor ( tcell . ColorWhite )
2020-06-24 18:37:32 +03:00
}
2021-02-17 22:17:13 +03:00
frame . SetTitle ( " TOFU " )
m . SetText (
2020-08-27 18:47:57 +03:00
//nolint:lll
2021-12-07 03:07:56 +03:00
fmt . Sprintf ( "%s's certificate has changed, possibly indicating a security issue. The certificate would have expired %s. Are you sure you want to continue? " ,
2020-07-11 01:59:51 +03:00
host ,
humanize . Time ( expiry ) ,
) ,
2020-06-22 03:37:27 +03:00
)
2021-12-08 04:18:08 +03:00
panels . ShowPanel ( PanelYesNoModal )
panels . SendToFront ( PanelYesNoModal )
2020-06-18 23:54:48 +03:00
App . SetFocus ( yesNoModal )
App . Draw ( )
resp := <- yesNoCh
2021-12-08 04:18:08 +03:00
panels . HidePanel ( PanelYesNoModal )
2020-07-29 01:09:36 +03:00
App . SetFocus ( tabs [ curTab ] . view )
App . Draw ( )
2020-06-18 23:54:48 +03:00
return resp
}