From a124b2702106ab24178b8877267c545e497615f2 Mon Sep 17 00:00:00 2001 From: equa Date: Tue, 11 Jul 2023 16:41:35 -0400 Subject: [PATCH 1/3] review: make application scrollable; add S to go back to previous entry --- cmd/review/main.go | 15 +++++++++++++++ go.mod | 7 +++---- go.sum | 15 --------------- 3 files changed, 18 insertions(+), 19 deletions(-) diff --git a/cmd/review/main.go b/cmd/review/main.go index d8e9e4d..63e4650 100644 --- a/cmd/review/main.go +++ b/cmd/review/main.go @@ -132,6 +132,7 @@ func _main() error { title.SetBackgroundColor(tcell.ColorBlack) appView := tview.NewTextView() + appView.SetScrollable(true) appView.SetDynamicColors(true) legend := tview.NewTextView() @@ -169,6 +170,11 @@ func _main() error { mainFlex.AddItem(title, 1, -1, false) mainFlex.AddItem(innerFlex, 0, 1, false) mainFlex.AddItem(bottomFlex, 1, -1, false) + // set scrollable + mainFlex.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey { + appView.InputHandler()(event, func (p tview.Primitive) {}) + return nil + }) pages := tview.NewPages() @@ -311,6 +317,15 @@ func _main() error { } updateCount() render() + return nil + case 'S': + signupIx-- + if signupIx < 0 { + signupIx = len(signups) - 1 + } + updateCount() + render() + return nil case 'r': if len(signups) > 0 { signupIx = rand.Intn(len(signups)) diff --git a/go.mod b/go.mod index 445a05c..bcebb12 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,9 @@ require ( github.com/AlecAivazis/survey/v2 v2.3.5 github.com/MakeNowJust/heredoc/v2 v2.0.1 github.com/charmbracelet/glamour v0.5.0 + github.com/charmbracelet/lipgloss v0.6.0 + github.com/gdamore/tcell/v2 v2.5.3 + github.com/mattn/go-sqlite3 v1.14.16 github.com/rivo/tview v0.0.0-20230130130022-4a1b7a76c01c github.com/spf13/cobra v1.5.0 gopkg.in/yaml.v3 v3.0.1 @@ -14,10 +17,8 @@ require ( require ( github.com/alecthomas/chroma v0.10.0 // indirect github.com/aymerick/douceur v0.2.0 // indirect - github.com/charmbracelet/lipgloss v0.6.0 // indirect github.com/dlclark/regexp2 v1.4.0 // indirect github.com/gdamore/encoding v1.0.0 // indirect - github.com/gdamore/tcell/v2 v2.5.3 // indirect github.com/gorilla/css v1.0.0 // indirect github.com/inconshreveable/mousetrap v1.0.0 // indirect github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect @@ -25,7 +26,6 @@ require ( github.com/mattn/go-colorable v0.1.2 // indirect github.com/mattn/go-isatty v0.0.14 // indirect github.com/mattn/go-runewidth v0.0.13 // indirect - github.com/mattn/go-sqlite3 v1.14.16 // indirect github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect github.com/microcosm-cc/bluemonday v1.0.17 // indirect github.com/muesli/reflow v0.3.0 // indirect @@ -39,5 +39,4 @@ require ( golang.org/x/sys v0.0.0-20220422013727-9388b58f7150 // indirect golang.org/x/term v0.0.0-20210503060354-a79de5458b56 // indirect golang.org/x/text v0.3.7 // indirect - gopkg.in/AlecAivazis/survey.v1 v1.8.8 // indirect ) diff --git a/go.sum b/go.sum index 71dc1e9..1e8f65c 100644 --- a/go.sum +++ b/go.sum @@ -1,9 +1,7 @@ -github.com/AlecAivazis/survey/v2 v2.0.5/go.mod h1:WYBhg6f0y/fNYUuesWQc0PKbJcEliGcYHB9sNT3Bg74= github.com/AlecAivazis/survey/v2 v2.3.5 h1:A8cYupsAZkjaUmhtTYv3sSqc7LO5mp1XDfqe5E/9wRQ= github.com/AlecAivazis/survey/v2 v2.3.5/go.mod h1:4AuI9b7RjAR+G7v9+C4YSlX/YL3K3cWNXgWXOhllqvI= github.com/MakeNowJust/heredoc/v2 v2.0.1 h1:rlCHh70XXXv7toz95ajQWOWQnN4WNLt0TdpZYIR/J6A= github.com/MakeNowJust/heredoc/v2 v2.0.1/go.mod h1:6/2Abh5s+hc3g9nbWLe9ObDIOhaRrqsyY9MWy+4JdRM= -github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8/go.mod h1:oX5x61PbNXchhh0oikYAH+4Pcfw5LKv21+Jnpr6r6Pc= github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2 h1:+vx7roKuyA63nhn5WAunQHLTznkw5W8b1Xc0dNjp83s= github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2/go.mod h1:HBCaDeC1lPdgDeDbhX8XFpy1jqjK0IBG8W5K+xYqA0w= github.com/alecthomas/chroma v0.10.0 h1:7XDcGkCQopCNKjZHfYrNLraA+M7e0fMiJ/Mfikbfjek= @@ -28,20 +26,17 @@ github.com/gdamore/tcell/v2 v2.5.3 h1:b9XQrT6QGbgI7JvZOJXFNczOQeIYbo8BfeSMzt2sAV github.com/gdamore/tcell/v2 v2.5.3/go.mod h1:wSkrPaXoiIWZqW/g7Px4xc79di6FTcpB8tvaKJ6uGBo= github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY= github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c= -github.com/hinshun/vt10x v0.0.0-20180616224451-1954e6464174/go.mod h1:DqJ97dSdRW1W22yXSB90986pcOyQ7r45iio1KN2ez1A= github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec h1:qv2VnGeEQHchGaZ/u7lxST/RaJw+cv273q79D81Xbog= github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec/go.mod h1:Q48J4R4DvxnHolD5P8pOtXigYlRuPLGl6moFx3ulM68= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= -github.com/kr/pty v1.1.4/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.13 h1:qdl+GuBjcsKKDco5BsxPJlId98mSWNKqYA+Co0SC1yA= github.com/mattn/go-isatty v0.0.13/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= @@ -59,7 +54,6 @@ github.com/microcosm-cc/bluemonday v1.0.17/go.mod h1:Z0r70sCuXHig8YpBzCc5eGHAap2 github.com/muesli/reflow v0.2.1-0.20210115123740-9e1d0d53df68/go.mod h1:Xk+z4oIWdQqJzsxyjgl3P22oYZnHdZ8FFTHAQQt5BMQ= github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s= github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8= -github.com/muesli/termenv v0.9.0 h1:wnbOaGz+LUR3jNT0zOzinPnyDaCZUQRZj9GxK8eRVl8= github.com/muesli/termenv v0.9.0/go.mod h1:R/LzAKf+suGs4IsO95y7+7DpFHO0KABgnZqtlyx2mBw= github.com/muesli/termenv v0.11.1-0.20220204035834-5ac8409525e0 h1:STjmj0uFfRryL9fzRA/OupNppeAID6QJYPMavTL7jtY= github.com/muesli/termenv v0.11.1-0.20220204035834-5ac8409525e0/go.mod h1:Bd5NYQ7pd+SrtBSrSNoBBmXlcY8+Xj4BMJgh8qcZrvs= @@ -79,7 +73,6 @@ github.com/spf13/cobra v1.5.0/go.mod h1:dWXEIy2H428czQCjInthrTRUg7yKbok+2Qi/yBIJ github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= @@ -88,15 +81,9 @@ github.com/yuin/goldmark v1.4.4 h1:zNWRjYUW32G9KirMXYHQHVNFkXvMI7LpgNW2AgYAoIs= github.com/yuin/goldmark v1.4.4/go.mod h1:rmuwmfZ0+bvzB24eSC//bk1R1Zp3hM0OXYv/G2LIilg= github.com/yuin/goldmark-emoji v1.0.1 h1:ctuWEyzGBwiucEqxzwe0SOYDXPAucOrE9NQC18Wa1os= github.com/yuin/goldmark-emoji v1.0.1/go.mod h1:2w1E6FEWLcDQkoTE+7HU6QF1F6SLlNGjRIBbIZQFqkQ= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20210614182718-04defd469f4e h1:XpT3nA5TvE525Ne3hInMh6+GETgn27Zfm9dxsThnX2Q= golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190530182044-ad28b68e88f1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -114,8 +101,6 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -gopkg.in/AlecAivazis/survey.v1 v1.8.8 h1:5UtTowJZTz1j7NxVzDGKTz6Lm9IWm8DDF6b7a2wq9VY= -gopkg.in/AlecAivazis/survey.v1 v1.8.8/go.mod h1:CaHjv79TCgAvXMSFJSVgonHXYWxnhzI3eoHtnX5UgUo= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= -- 2.47.0 From 0472c24199ca3eeb4f276d1ff42d5fd3afc0342c Mon Sep 17 00:00:00 2001 From: equa Date: Tue, 11 Jul 2023 22:08:22 -0400 Subject: [PATCH 2/3] basic email validation the code for the panes is getting messy. since we have one window we constantly reuse, and especially now that we have weird state-dependent validation. probably good to rework? --- cmd/review/main.go | 33 +++++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/cmd/review/main.go b/cmd/review/main.go index 63e4650..9495db2 100644 --- a/cmd/review/main.go +++ b/cmd/review/main.go @@ -136,7 +136,7 @@ func _main() error { appView.SetDynamicColors(true) legend := tview.NewTextView() - legend.SetText("s: skip r: random A: approve R: reject N: notate Q: quit") + legend.SetText("s/S: next/prev r: random A: approve R: reject N: notate Q: quit") legend.SetTextColor(tcell.ColorPurple) legend.SetTextAlign(tview.AlignCenter) legend.SetBackgroundColor(tcell.ColorBlack) @@ -172,7 +172,7 @@ func _main() error { mainFlex.AddItem(bottomFlex, 1, -1, false) // set scrollable mainFlex.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey { - appView.InputHandler()(event, func (p tview.Primitive) {}) + appView.InputHandler()(event, func(p tview.Primitive) {}) return nil }) @@ -231,21 +231,18 @@ func _main() error { decisionFI.SetOptions([]string{"accepted", "rejected"}, func(_ string, _ int) {}) cleanEmailInput := tview.NewInputField() - cleanEmailInput.SetLabel("clean email") + cleanEmailInput.SetLabel("clean email ") cleanEmailInput.SetAcceptanceFunc(func(tx string, _ rune) bool { return len(tx) > 0 }) reviewForm.AddFormItem(decisionFI) reviewForm.AddFormItem(cleanEmailInput) reviewForm.AddButton("submit", func() { currSignup := signups[signupIx] - cleanEmail := reviewForm.GetFormItemByLabel("clean email").(*tview.InputField).GetText() - defer func() { - cleanEmailInput.SetText("") - }() + cleanEmail := cleanEmailInput.GetText() currSignup.CleanEmail = cleanEmail decision := models.SignupRejected - _, d := reviewForm.GetFormItemByLabel("decision").(*tview.DropDown).GetCurrentOption() + _, d := decisionFI.GetCurrentOption() if d == "accepted" { decision = models.SignupAccepted } @@ -332,12 +329,16 @@ func _main() error { updateCount() render() } + // TODO: there's a bunch of messy state management. + // should we generate this pane functionally? case 'A': if len(signups) == 0 { return nil } emailVal := signups[signupIx].Email providedEmailView.SetText(emailVal) + cleanEmailInput.SetLabel("clean email ") + cleanEmailInput.SetText("") /* TODO the placeholder doesn't appear to become the default text which is what I wanted it to do. Just taking this out so the blank box beckons @@ -346,6 +347,13 @@ func _main() error { cleanEmailInput.SetPlaceholder( strings.TrimSpace(strings.ReplaceAll(emailVal, "\n", " "))) */ + cleanEmailInput.SetChangedFunc(func(text string) { + if strings.Contains(emailVal, text) { + cleanEmailInput.SetLabel("clean email ") + } else { + cleanEmailInput.SetLabel("[red]clean email :(") + } + }) decisionFI.SetCurrentOption(0) pages.SwitchToPage("review") app.SetFocus(cleanEmailInput) @@ -356,6 +364,8 @@ func _main() error { } emailVal := signups[signupIx].Email providedEmailView.SetText(emailVal) + cleanEmailInput.SetLabel("clean email ") + cleanEmailInput.SetText("") /* TODO the placeholder doesn't appear to become the default text which is what I wanted it to do. Just taking this out so the blank box beckons @@ -364,6 +374,13 @@ func _main() error { cleanEmailInput.SetPlaceholder( strings.TrimSpace(strings.ReplaceAll(emailVal, "\n", " "))) */ + cleanEmailInput.SetChangedFunc(func(text string) { + if strings.Contains(emailVal, text) { + cleanEmailInput.SetLabel("clean email ") + } else { + cleanEmailInput.SetLabel("[red]clean email :(") + } + }) decisionFI.SetCurrentOption(1) pages.SwitchToPage("review") app.SetFocus(cleanEmailInput) -- 2.47.0 From 1e7a016dca7dd177dc43758e76bfe7280ec5e2ed Mon Sep 17 00:00:00 2001 From: equa Date: Wed, 12 Jul 2023 10:33:27 -0400 Subject: [PATCH 3/3] add search function --- cmd/review/main.go | 63 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 62 insertions(+), 1 deletion(-) diff --git a/cmd/review/main.go b/cmd/review/main.go index 9495db2..f6195f4 100644 --- a/cmd/review/main.go +++ b/cmd/review/main.go @@ -1,12 +1,15 @@ package main import ( + "bytes" "database/sql" "errors" "fmt" "math/rand" "os" + "os/exec" "os/user" + "strconv" "strings" "time" @@ -88,6 +91,42 @@ func renderNotes(s models.TownSignup) string { return out } +func searchSignups(signups []*models.TownSignup) (int, error) { + escapeNuls := func(str string) string { + return strings.ReplaceAll(str, "\000", " ") + } + + buf := new(bytes.Buffer) + + for ix, signup := range signups { + fmt.Fprintf(buf, "%d\t%s\000", ix, escapeNuls(signup.Email)) + fmt.Fprintf(buf, "%d\t%s\000", ix, escapeNuls(signup.How)) + fmt.Fprintf(buf, "%d\t%s\000", ix, escapeNuls(signup.Links)) + fmt.Fprintf(buf, "%d\t%s\000", ix, escapeNuls(signup.Why)) + } + + cmd := exec.Command("fzf", "--read0", "--delimiter=\t", "--tac", "--with-nth=2..") + cmd.Stdin = buf + cmd.Stderr = os.Stderr + + out, err := cmd.Output() + if err != nil { + return -1, err + } + + if len(out) == 0 { + return -1, nil + } + + s := strings.Split(string(out[:]), "\t")[0] + n, err := strconv.Atoi(s) + if err != nil { + return -1, err + } + + return n, nil +} + func _main() error { inviteDB, err := invites.ConnectDB() if err != nil { @@ -136,7 +175,7 @@ func _main() error { appView.SetDynamicColors(true) legend := tview.NewTextView() - legend.SetText("s/S: next/prev r: random A: approve R: reject N: notate Q: quit") + legend.SetText("s/S: next/prev r: random F: find A: approve R: reject N: notate Q: quit") legend.SetTextColor(tcell.ColorPurple) legend.SetTextAlign(tview.AlignCenter) legend.SetBackgroundColor(tcell.ColorBlack) @@ -391,6 +430,28 @@ func _main() error { } pages.SwitchToPage("notate") return nil + case 'F': + app.Suspend(func() { + ix, err := searchSignups(signups) + if err != nil { + if exiterr, ok := err.(*exec.ExitError); ok { + // no match or interrupt. who cares + switch exiterr.ExitCode() { + case 1: case 130: + return + } + } + + errorModal.SetText(fmt.Sprintf("error! failed to search: %s", err.Error())) + pages.SwitchToPage("error") + } else if ix >= 0 { + signupIx = ix + } + }) + + updateCount() + render() + return nil case 'Q': app.Stop() } -- 2.47.0