diff --git a/actionsList.go b/actionsList.go index 7134156..674cc5f 100644 --- a/actionsList.go +++ b/actionsList.go @@ -44,7 +44,7 @@ func (m ActionListPageModel) Update(msg tea.Msg, ctx *context) (ActionListPageMo if m.cursor < len(actions)-1 { m.cursor++ } - case "enter", " ": + case "enter", " ", "l": action := actions[m.cursor] switch action { case Write: @@ -56,7 +56,7 @@ func (m ActionListPageModel) Update(msg tea.Msg, ctx *context) (ActionListPageMo actionToTakeCmd := func() tea.Msg { return SelectActionToTake{ActionToTake: action} } - loadGemLogsCmd := gemlog.LoadGemlogCMD(ctx.config) + loadGemLogsCmd := gemlog.LoadGemlogsCMD(ctx.config) return m, tea.Batch(switchPageCmd, loadGemLogsCmd, actionToTakeCmd) } } diff --git a/bruno/Gemlog/Get Gemlog.bru b/bruno/Gemlog/Get Gemlog.bru new file mode 100644 index 0000000..019a53b --- /dev/null +++ b/bruno/Gemlog/Get Gemlog.bru @@ -0,0 +1,20 @@ +meta { + name: Get Gemlog + type: http + seq: 5 +} + +get { + url: http://eisenhorn:5023/gemlog/d198245cbc0d67891f3d3d6dc301c242 + body: json + auth: basic +} + +auth:basic { + username: gemlog-cli + password: {{pw}} +} + +settings { + encodeUrl: true +} diff --git a/bruno/Gemlog/Update Gemlog.bru b/bruno/Gemlog/Update Gemlog.bru new file mode 100644 index 0000000..584bcbc --- /dev/null +++ b/bruno/Gemlog/Update Gemlog.bru @@ -0,0 +1,33 @@ +meta { + name: Update Gemlog + type: http + seq: 4 +} + +put { + url: http://eisenhorn:5023/gemlog/d198245cbc0d67891f3d3d6dc301c242?rev=2-e997af4bb2b1b18ce224ebcb46d145dc + body: json + auth: basic +} + +params:query { + rev: 2-e997af4bb2b1b18ce224ebcb46d145dc +} + +auth:basic { + username: gemlog-cli + password: {{pw}} +} + +body:json { + { + "title": "Bruno Test Updated", + "slug": "bruno-test", + "date": "2025-09-30T21:04:45.092Z", + "gemtxt": "this is a test\n\nfrom bruno\nwith someupdated data" + } +} + +settings { + encodeUrl: true +} diff --git a/entry.go b/entry.go new file mode 100644 index 0000000..e69de29 diff --git a/entryList.go b/entryList.go index a579e65..ce76bc0 100644 --- a/entryList.go +++ b/entryList.go @@ -15,18 +15,14 @@ type EntryListPageModel struct { type SelectActionToTake struct{ ActionToTake Action } -func InitialEntryListPageModel() EntryListPageModel { +func initialEntryListPageModel() EntryListPageModel { return EntryListPageModel{ cursor: 0, actionToTake: Read, } } -func (m EntryListPageModel) InitEntryListPage() tea.Cmd { - return nil -} - -func (m EntryListPageModel) Update(msg tea.Msg, ctx *context) (EntryListPageModel, tea.Cmd) { +func (m EntryListPageModel) Update(msg tea.Msg, active bool, ctx *context) (EntryListPageModel, tea.Cmd) { switch msg := msg.(type) { case SelectActionToTake: m.actionToTake = msg.ActionToTake @@ -34,6 +30,9 @@ func (m EntryListPageModel) Update(msg tea.Msg, ctx *context) (EntryListPageMode m.entries = msg.Logs return m, nil case tea.KeyMsg: + if !active { + return m, nil + } switch msg.String() { case "up", "k": if m.cursor > 0 { @@ -43,33 +42,27 @@ func (m EntryListPageModel) Update(msg tea.Msg, ctx *context) (EntryListPageMode if m.cursor < len(actions)-1 { m.cursor++ } + case "left", "h": + cmd := func() tea.Msg { + return SwitchPages{Page: ActionList} + } + return m, cmd + case "enter", " ": id := m.entries[m.cursor].ID + rev := m.entries[m.cursor].Rev switch m.actionToTake { case Delete: - return m, gemlog.DeleteGemlogCMD(ctx.config, id) - } + delCmd := gemlog.DeleteGemlogCMD(ctx.config, id, rev) + loadCmd := gemlog.LoadGemlogsCMD(ctx.config) + navCmd := func() tea.Msg { + return SwitchPages{Page: ActionList} + } - // action := actions[m.cursor] - // switch action { - // case Write: - // return m, gemlog.WritePostCMD(ctx.config) - // case Read: - // m.ui.page = EntryList - // m.ui.entryListPage.cursor = 0 - // m.ui.entryListPage.actionToTake = Read - // return m, gemlog.LoadGemlogCMD(ctx.config) - // case Edit: - // m.ui.page = EntryList - // m.ui.entryListPage.cursor = 0 - // m.ui.entryListPage.actionToTake = Edit - // return m, gemlog.LoadGemlogCMD(ctx.config) - // case Delete: - // m.ui.page = EntryList - // m.ui.entryListPage.cursor = 0 - // m.ui.entryListPage.actionToTake = Delete - // return m, gemlog.LoadGemlogCMD(m.context.config) - // } + return m, tea.Sequence(delCmd, loadCmd, navCmd) + case Read: + return m, gemlog.LoadGemlogCMD(ctx.config, id) + } } } @@ -85,5 +78,7 @@ func (m EntryListPageModel) View() string { } s += fmt.Sprintf("%s %s : %s\n", cursor, entry.Date, entry.Slug) } + + s += "\n\n Press h or left arrow to go back" return s } diff --git a/gemlog/common.go b/gemlog/common.go index e7d44f1..363215d 100644 --- a/gemlog/common.go +++ b/gemlog/common.go @@ -3,3 +3,4 @@ package gemlog type Notification string type ErrorMsg struct{ err error } type GemLogsLoaded struct{ Logs []GemlogListEntry } +type GemLogLoaded struct{ Log GemlogEntry } diff --git a/gemlog/core.go b/gemlog/core.go index 4501a97..4b369c6 100644 --- a/gemlog/core.go +++ b/gemlog/core.go @@ -21,6 +21,7 @@ type GemlogListEntry struct { Slug string `json:"slug"` Date time.Time `json:"date"` ID string `json:"id"` + Rev string `json:"rev"` } // NewUUID generates a new UUID v4 using crypto/rand diff --git a/gemlog/db.go b/gemlog/db.go index 2c9a18c..f8bcc8c 100644 --- a/gemlog/db.go +++ b/gemlog/db.go @@ -51,6 +51,7 @@ func listGemLogs(config *Config) ([]GemlogListEntry, error) { Title string `json:"title"` Slug string `json:"slug"` Date time.Time `json:"date"` + Rev string `json:"rev"` ID string `json:"id"` } `json:"value"` } `json:"rows"` @@ -68,6 +69,7 @@ func listGemLogs(config *Config) ([]GemlogListEntry, error) { Title: row.Value.Title, Slug: row.Value.Slug, Date: row.Value.Date, + Rev: row.Value.Rev, } entries = append(entries, entry) } @@ -76,8 +78,54 @@ func listGemLogs(config *Config) ([]GemlogListEntry, error) { return entries, nil } -func DeleteGemlogEntry(config *Config, id string) error { +func ReadGemlogEntry(config *Config, id string) (GemlogEntry, error) { url := fmt.Sprintf("%s:%d/gemlog/%s", config.CouchDB.Host, config.CouchDB.Port, id) + req, err := http.NewRequest("GET", url, nil) + if err != nil { + return GemlogEntry{}, fmt.Errorf("failed to create request: %w", err) + } + + req.Header.Add("authorization", genBasicAuthHeader(config.CouchDB.User, config.CouchDB.Password)) + req.Header.Add("content-type", "application/json") + + res, err := http.DefaultClient.Do(req) + if err != nil { + return GemlogEntry{}, fmt.Errorf("failed to send request: %w", err) + } + defer res.Body.Close() + + body, err := io.ReadAll(res.Body) + if err != nil { + return GemlogEntry{}, fmt.Errorf("failed to read response body: %w", err) + } + + if res.StatusCode < 200 || res.StatusCode >= 300 { + return GemlogEntry{}, fmt.Errorf("unexpected status code %d: %s", res.StatusCode, string(body)) + } + + var rawData struct { + ID int `json:"_id"` + Rev int `json:"_rev"` + Title string `json:"title"` + GemText string `json:"gemtext"` + Slug string `json:"slug"` + Date time.Time `json:"date"` + } + + if err := json.Unmarshal(body, &rawData); err != nil { + return GemlogEntry{}, fmt.Errorf("failed to parse response: %w", err) + } + + return GemlogEntry{ + Title: rawData.Title, + Slug: rawData.Slug, + Date: rawData.Date, + Tags: make([]string, 0), + }, nil +} + +func DeleteGemlogEntry(config *Config, id string, rev string) error { + url := fmt.Sprintf("%s:%d/gemlog/%s?rev=%s", config.CouchDB.Host, config.CouchDB.Port, id, rev) req, err := http.NewRequest("DELETE", url, nil) if err != nil { return fmt.Errorf("failed to create request: %w", err) diff --git a/gemlog/list.go b/gemlog/read.go similarity index 50% rename from gemlog/list.go rename to gemlog/read.go index e28d213..173fc7b 100644 --- a/gemlog/list.go +++ b/gemlog/read.go @@ -6,17 +6,28 @@ import ( tea "github.com/charmbracelet/bubbletea" ) -func DeleteGemlogCMD(config *Config, id string) tea.Cmd { +func DeleteGemlogCMD(config *Config, id string, rev string) tea.Cmd { return func() tea.Msg { - err := DeleteGemlogEntry(config, id) + err := DeleteGemlogEntry(config, id, rev) if err != nil { return ErrorMsg{err} } + return Notification(fmt.Sprintf("Gemlog with id: %s deleted", id)) } } -func LoadGemlogCMD(config *Config) tea.Cmd { +func LoadGemlogCMD(config *Config, id string) tea.Cmd { + return func() tea.Msg { + log, err := ReadGemlogEntry(config, id) + if err != nil { + return ErrorMsg{err} + } + return GemLogLoaded{Log: log} + } +} + +func LoadGemlogsCMD(config *Config) tea.Cmd { return func() tea.Msg { logs, err := listGemLogs(config) if err != nil { diff --git a/main.go b/main.go index 6b2d5e8..953e0c8 100644 --- a/main.go +++ b/main.go @@ -14,7 +14,7 @@ type Page string const ( ActionList Page = "actionList" EntryList Page = "entryList" - // Entry Page = "entry" + Entry Page = "entry" ) type SwitchPages struct{ Page Page } @@ -40,12 +40,10 @@ type model struct { func initialModel(config *gemlog.Config) model { return model{ ui: uiState{ - page: ActionList, - // entryListPage: entryListPageModel{ - // cursor: 0, - // entries: []gemlog.GemlogListEntry{}, - // }, + page: ActionList, actionListPage: initialActionListPageModel(), + entryListPage: initialEntryListPageModel(), + // entryPage: initialEntryPageModel(), }, context: &context{ config: config, @@ -80,7 +78,7 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { m.ui.actionListPage = actionListM cmds = append(cmds, cmd) - entryListM, cmd := m.ui.entryListPage.Update(msg, m.context) + entryListM, cmd := m.ui.entryListPage.Update(msg, m.ui.page == EntryList, m.context) m.ui.entryListPage = entryListM cmds = append(cmds, cmd)