Skip to content

Commit

Permalink
Split views in files
Browse files Browse the repository at this point in the history
  • Loading branch information
edgarlatorre committed Feb 11, 2024
1 parent 1ee269d commit dff66f4
Show file tree
Hide file tree
Showing 4 changed files with 244 additions and 43 deletions.
87 changes: 44 additions & 43 deletions cmd/bookmark/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,48 +4,52 @@ import (
"fmt"
"os"

"github.com/charmbracelet/bubbles/key"
"github.com/charmbracelet/bubbles/list"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
"github.com/edgarlatorre/bookmark/internal/repositories"
"os/exec"
"github.com/edgarlatorre/bookmark/internal/models"
)

var docStyle = lipgloss.NewStyle().Margin(1, 2)

type item struct {
title, url string
type State uint

const (
listView State = iota
formView
)

type keymap struct {
Create key.Binding
}

func (i item) Title() string { return i.title }
func (i item) Description() string { return i.url }
func (i item) FilterValue() string { return i.title }
var Keymap = keymap{
Create: key.NewBinding(
key.WithKeys("n"),
key.WithHelp("n", "New url"),
),
}

type model struct {
list list.Model
state State
list list.Model
form models.FormModel
}

func initialData() model {
urls, err := repositories.Read("urls.json")

if err != nil {
fmt.Println("Error to read json file:", err)

m := model{list: list.New(nil, list.NewDefaultDelegate(), 0, 0)}
m.list.Title = "Bookmark"

return m
m := model{
state: listView,
list: models.NewListModel(),
form: models.NewFormModel(),
}

items := make([]list.Item, len(urls))

for i, u := range urls {
items[i] = item{title: u.Name, url: u.Url}
m.list.AdditionalShortHelpKeys = func() []key.Binding {
return []key.Binding{
Keymap.Create,
}
}

m := model{list: list.New(items, list.NewDefaultDelegate(), 0, 0)}
m.list.Title = "Bookmark"

return m
}

Expand All @@ -62,33 +66,30 @@ func main() {
}

func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
var cmd tea.Cmd

if m.state == listView {
m.list, cmd = models.UpdateList(m.list, msg)
} else {
m.form, cmd = models.UpdateForm(m.form, msg)
}
switch msg := msg.(type) {
case tea.KeyMsg:
switch msg.String() {
case "ctrl+c", "q":
return m, tea.Quit
case "enter", " ":
if item, ok := m.list.SelectedItem().(item); ok {
cmd := exec.Command("open", item.Description())
_, err := cmd.Output()

if err != nil {
fmt.Println(err.Error())
}
} else {
fmt.Println("Not found")
}
case "n":
m.state = formView
case "esc", "back":
m.state = listView
}
case tea.WindowSizeMsg:
h, v := docStyle.GetFrameSize()
m.list.SetSize(msg.Width-h, msg.Height-v)
}

var cmd tea.Cmd
m.list, cmd = m.list.Update(msg)
return m, cmd
}

func (m model) View() string {
return docStyle.Render(m.list.View())
if m.state == listView {
return m.list.View()
} else {
return m.form.View()
}
}
80 changes: 80 additions & 0 deletions internal/models/form.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package models

import (
"fmt"
"strings"

"github.com/charmbracelet/bubbles/cursor"
"github.com/charmbracelet/bubbles/textinput"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
)

var (
focusedStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("205"))
blurredStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("240"))
cursorStyle = focusedStyle.Copy()
noStyle = lipgloss.NewStyle()
helpStyle = blurredStyle.Copy()
cursorModeHelpStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("244"))

focusedButton = focusedStyle.Copy().Render("[ Submit ]")
blurredButton = fmt.Sprintf("[ %s ]", blurredStyle.Render("Submit"))
)

type FormModel struct {
focusIndex int
form []textinput.Model
cursorMode cursor.Mode
}

func NewFormModel() FormModel {
t := textinput.New()
t.Placeholder = "Url"
t.Focus()

m := FormModel{
form: []textinput.Model{t},
}

return m
}

func (m FormModel) Init() tea.Cmd {
return textinput.Blink
}

func UpdateForm(m FormModel, msg tea.Msg) (FormModel, tea.Cmd) {
cmd := m.updateInputs(msg)

return m, cmd
}

func (m *FormModel) updateInputs(msg tea.Msg) tea.Cmd {
cmds := make([]tea.Cmd, len(m.form))

for i := range m.form {
m.form[i], cmds[i] = m.form[i].Update(msg)
}

return tea.Batch(cmds...)
}

func (m FormModel) View() string {
var b strings.Builder
inputStyle := lipgloss.NewStyle().
Background(lipgloss.Color("62")).
Foreground(lipgloss.Color("230")).
Padding(0, 1)
b.WriteString(" " + inputStyle.Render("Bookmark"))
b.WriteString("\n\n")

for i := range m.form {
b.WriteString(" " + m.form[i].View())
}

button := &blurredButton
fmt.Fprintf(&b, "\n\n %s\n\n", *button)

return b.String()
}
103 changes: 103 additions & 0 deletions internal/models/list.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
package models

import (
"fmt"
"os/exec"

"github.com/charmbracelet/bubbles/key"
"github.com/charmbracelet/bubbles/list"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
"github.com/edgarlatorre/bookmark/internal/repositories"
)

var docStyle = lipgloss.NewStyle().Margin(1, 2)

type keymap struct {
Create key.Binding
}

var Keymap = keymap{
Create: key.NewBinding(
key.WithKeys("n"),
key.WithHelp("n", "New url"),
),
}

type item struct {
title, url string
}

type ListModel struct {
model list.Model
}

func (i item) Title() string { return i.title }
func (i item) Description() string { return i.url }
func (i item) FilterValue() string { return i.title }

func NewListModel() list.Model {
urls, err := repositories.Read("urls.json")
m := list.Model{}

if err != nil {
fmt.Println("Error to read json file:", err)

return m
}

items := make([]list.Item, len(urls))

for i, u := range urls {
items[i] = item{title: u.Name, url: u.Url}
}

m = list.New(items, list.NewDefaultDelegate(), 0, 0)
m.Title = "Bookmark"
m.AdditionalShortHelpKeys = func() []key.Binding {
return []key.Binding{
Keymap.Create,
}
}

return m
}

func UpdateList(m list.Model, msg tea.Msg) (list.Model, tea.Cmd) {
switch msg := msg.(type) {
case tea.KeyMsg:
switch msg.String() {
case "ctrl+c", "q":
return m, tea.Quit
case "c":
return m, tea.Quit
case "enter", " ":
if item, ok := m.SelectedItem().(item); ok {
cmd := exec.Command("open", item.Description())
_, err := cmd.Output()

if err != nil {
fmt.Println(err.Error())
}
} else {
fmt.Println("Not found")
}
}
case tea.WindowSizeMsg:
h, v := docStyle.GetFrameSize()
m.SetSize(msg.Width-h, msg.Height-v)
}

var cmd tea.Cmd
m, cmd = m.Update(msg)

return m, cmd
}

func (m ListModel) Init() tea.Cmd {
return nil
}

func (m ListModel) View() string {
return m.model.View()
}
17 changes: 17 additions & 0 deletions test/internal/models/form_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package models

import (
"strings"
"testing"

"github.com/edgarlatorre/bookmark/internal/models"
)

func TestFormModelView(t *testing.T) {
m := models.NewFormModel()
content := m.View()

if !strings.Contains(content, "Url") {
t.Fatalf(`View() does not contain Url, %s, error`, content)
}
}

0 comments on commit dff66f4

Please sign in to comment.