add search, scrolling, forward/backward navigation #2
|
@ -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 {
|
||||
|
@ -132,10 +171,11 @@ func _main() error {
|
|||
title.SetBackgroundColor(tcell.ColorBlack)
|
||||
|
||||
appView := tview.NewTextView()
|
||||
appView.SetScrollable(true)
|
||||
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 F: find A: approve R: reject N: notate Q: quit")
|
||||
legend.SetTextColor(tcell.ColorPurple)
|
||||
legend.SetTextAlign(tview.AlignCenter)
|
||||
legend.SetBackgroundColor(tcell.ColorBlack)
|
||||
|
@ -169,6 +209,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()
|
||||
|
||||
|
@ -232,14 +277,11 @@ func _main() error {
|
|||
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
|
||||
}
|
||||
|
@ -311,18 +353,31 @@ 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))
|
||||
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
|
||||
|
@ -331,6 +386,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)
|
||||
|
@ -341,6 +403,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
|
||||
|
@ -349,6 +413,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)
|
||||
|
@ -359,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()
|
||||
}
|
||||
|
|
7
go.mod
7
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
|
||||
)
|
||||
|
|
15
go.sum
15
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=
|
||||
|
|
Loading…
Reference in New Issue