filter.go 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  1. package filter
  2. import (
  3. "fmt"
  4. "git.andreafazzi.eu/andrea/probo/pkg/store"
  5. "git.andreafazzi.eu/andrea/probo/pkg/store/file"
  6. "github.com/charmbracelet/bubbles/help"
  7. "github.com/charmbracelet/bubbles/key"
  8. tea "github.com/charmbracelet/bubbletea"
  9. "github.com/charmbracelet/lipgloss"
  10. "github.com/remogatto/sugarfoam/components/group"
  11. "github.com/remogatto/sugarfoam/components/header"
  12. "github.com/remogatto/sugarfoam/components/statusbar"
  13. "github.com/remogatto/sugarfoam/components/table"
  14. "github.com/remogatto/sugarfoam/components/textinput"
  15. "github.com/remogatto/sugarfoam/components/viewport"
  16. "github.com/remogatto/sugarfoam/layout"
  17. "github.com/remogatto/sugarfoam/layout/tiled"
  18. )
  19. type Store interface {
  20. }
  21. type storeLoadedMsg struct {
  22. store file.FileStorer[store.Storable]
  23. }
  24. type FilterModel struct {
  25. // UI
  26. group *group.Model
  27. help help.Model
  28. statusBar *statusbar.Model
  29. // Layout
  30. document *layout.Layout
  31. // Key bindings
  32. bindings *keyBindings
  33. // Store
  34. store file.FileStorer[store.Storable]
  35. state int
  36. }
  37. type keyBindings struct {
  38. group *group.Model
  39. quit key.Binding
  40. }
  41. func (k *keyBindings) ShortHelp() []key.Binding {
  42. keys := make([]key.Binding, 0)
  43. current := k.group.Current()
  44. switch item := current.(type) {
  45. case *table.Model:
  46. keys = append(
  47. keys,
  48. item.KeyMap.LineUp,
  49. item.KeyMap.LineDown,
  50. )
  51. }
  52. keys = append(
  53. keys,
  54. k.quit,
  55. )
  56. return keys
  57. }
  58. func (k keyBindings) FullHelp() [][]key.Binding {
  59. return [][]key.Binding{
  60. {
  61. k.quit,
  62. },
  63. }
  64. }
  65. func newBindings(g *group.Model) *keyBindings {
  66. return &keyBindings{
  67. group: g,
  68. quit: key.NewBinding(
  69. key.WithKeys("esc"), key.WithHelp("esc", "Quit app"),
  70. ),
  71. }
  72. }
  73. func New() *FilterModel {
  74. textinput := textinput.New(
  75. textinput.WithPlaceholder("Write your jq filter here..."),
  76. )
  77. table := table.New()
  78. viewport := viewport.New()
  79. help := help.New()
  80. group := group.New(
  81. group.WithItems(textinput, viewport, table),
  82. group.WithLayout(
  83. layout.New(
  84. layout.WithStyles(&layout.Styles{Container: lipgloss.NewStyle().Padding(1, 0, 1, 0)}),
  85. layout.WithItem(textinput),
  86. layout.WithItem(tiled.New(viewport, table)),
  87. ),
  88. ),
  89. )
  90. bindings := newBindings(group)
  91. statusBar := statusbar.New(bindings)
  92. header := header.New(
  93. header.WithContent(
  94. lipgloss.NewStyle().
  95. Bold(true).
  96. Border(lipgloss.NormalBorder(), false, false, true, false).
  97. Render("Participants filter 👫"),
  98. ),
  99. )
  100. document := layout.New(
  101. layout.WithStyles(&layout.Styles{Container: lipgloss.NewStyle().Margin(1)}),
  102. layout.WithItem(header),
  103. layout.WithItem(group),
  104. layout.WithItem(statusBar),
  105. )
  106. return &FilterModel{
  107. group: group,
  108. statusBar: statusBar,
  109. document: document,
  110. bindings: bindings,
  111. help: help,
  112. }
  113. }
  114. func (m *FilterModel) Init() tea.Cmd {
  115. var cmds []tea.Cmd
  116. cmds = append(cmds, m.group.Init(), loadParticipantStore())
  117. m.group.Focus()
  118. return tea.Batch(cmds...)
  119. }
  120. func (m *FilterModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
  121. var cmds []tea.Cmd
  122. switch msg := msg.(type) {
  123. case tea.WindowSizeMsg:
  124. m.document.SetSize(msg.Width, msg.Height)
  125. case tea.KeyMsg:
  126. switch {
  127. case key.Matches(msg, m.bindings.quit):
  128. return m, tea.Quit
  129. }
  130. case storeLoadedMsg:
  131. m.handleStoreLoaded(msg)
  132. }
  133. cmds = m.handleState(msg, cmds)
  134. return m, tea.Batch(cmds...)
  135. }
  136. func (m *FilterModel) View() string {
  137. return m.document.View()
  138. }
  139. func (m *FilterModel) handleStoreLoaded(msg tea.Msg) {
  140. storeMsg := msg.(storeLoadedMsg)
  141. m.store = storeMsg.store
  142. m.state = FilterState
  143. }
  144. func (m *FilterModel) handleState(msg tea.Msg, cmds []tea.Cmd) []tea.Cmd {
  145. _, cmd := m.group.Update(msg)
  146. cmds = append(cmds, cmd)
  147. m.statusBar.SetContent(
  148. formats[FilterState][0],
  149. fmt.Sprintf(formats[FilterState][1], len(m.store.ReadAll())),
  150. formats[FilterState][2],
  151. )
  152. return cmds
  153. }
  154. func loadParticipantStore() tea.Cmd {
  155. return func() tea.Msg {
  156. pStore, err := file.NewDefaultParticipantFileStore()
  157. if err != nil {
  158. panic(err)
  159. }
  160. return storeLoadedMsg{pStore}
  161. }
  162. }