Create/Delete note, Clean up code

This commit is contained in:
MrLetsplay 2023-08-26 22:12:48 +02:00
parent 3ef0fbe76f
commit e7d12dc1d7
Signed by: mr
SSH Key Fingerprint: SHA256:92jBH80vpXyaZHjaIl47pjRq+Yt7XGTArqQg1V7hSqg
2 changed files with 258 additions and 120 deletions

105
api.go Normal file
View File

@ -0,0 +1,105 @@
package main
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
)
type note struct {
Id int64 `json:"id"`
Title string `json:"title"`
Content string `json:"content"`
}
const endpoint = "http://localhost:8080/api"
func loadNotes() ([]note, error) {
res, err := http.Get(endpoint + "/notes")
if err != nil {
return []note{}, nil
}
data, err := io.ReadAll(res.Body)
if err != nil {
return []note{}, nil
}
var loadedNotes []note
json.Unmarshal(data, &loadedNotes)
return loadedNotes, nil
}
func putNote(n note) error {
json, err := json.Marshal(n)
if err != nil {
return err
}
req, err := http.NewRequest("PUT", endpoint+"/notes/"+fmt.Sprint(n.Id), bytes.NewReader(json))
if err != nil {
return err
}
req.Header.Add("Content-Type", "application/json")
res, err := http.DefaultClient.Do(req)
if err != nil {
return err
}
if res.StatusCode != http.StatusOK {
return errors.New("Request failed: " + res.Status)
}
return nil
}
func postNote(n note) (note, error) {
jsonBytes, err := json.Marshal(n)
if err != nil {
return note{}, err
}
res, err := http.Post(endpoint+"/notes", "application/json", bytes.NewReader(jsonBytes))
if err != nil {
return note{}, err
}
if res.StatusCode != http.StatusCreated {
return note{}, errors.New("Request failed: " + res.Status)
}
resBytes, err := io.ReadAll(res.Body)
if err != nil {
return note{}, err
}
err = json.Unmarshal(resBytes, &n)
if err != nil {
return note{}, err
}
return n, nil
}
func deleteNote(n note) error {
req, err := http.NewRequest("DELETE", endpoint+"/notes/"+fmt.Sprint(n.Id), bytes.NewReader([]byte{}))
if err != nil {
return err
}
res, err := http.DefaultClient.Do(req)
if err != nil {
return err
}
if res.StatusCode != http.StatusOK {
return errors.New("Request failed: " + res.Status)
}
return nil
}

219
main.go
View File

@ -1,13 +1,9 @@
package main package main
import ( import (
"bytes"
"encoding/json"
"errors"
"fmt" "fmt"
"io" "io"
"log" "log"
"net/http"
"os" "os"
"os/exec" "os/exec"
"strings" "strings"
@ -17,72 +13,18 @@ import (
"github.com/mattn/go-runewidth" "github.com/mattn/go-runewidth"
) )
// TODO new, delete
type note struct {
Id int64 `json:"id"`
Title string `json:"title"`
Content string `json:"content"`
}
const lineHeight = 2 const lineHeight = 2
const editor = "vim" const editor = "vim"
const endpoint = "http://localhost:8080/api" const (
uiMain = 0
uiDelete = 1
)
var uiState = uiMain
var selectedNote = 0 var selectedNote = 0
var notes []note var notes []note
func emitStr(s tcell.Screen, x int, y int, style tcell.Style, str string) {
for _, c := range str {
var comb []rune
w := runewidth.RuneWidth(c)
if w == 0 {
comb = []rune{c}
c = ' '
w = 1
}
s.SetContent(x, y, c, comb, style)
x += w
}
}
func showNotes(s tcell.Screen) {
s.Fill(' ', tcell.StyleDefault)
for idx, note := range notes {
style := tcell.StyleDefault
if idx == selectedNote {
style = style.Foreground(tcell.ColorBlack).Background(tcell.ColorWhite)
}
text := fmt.Sprint(idx) + ". " + note.Title
emitStr(s, 1, 1+idx*lineHeight, style, text)
}
_, h := s.Size()
emitStr(s, 1, h-1, tcell.StyleDefault.Foreground(tcell.ColorBlue), "n - new, r - rename, d - delete, Enter - edit, Esc - exit")
}
func loadNotes() {
res, err := http.Get(endpoint + "/notes")
if err != nil {
log.Fatalln(err)
}
data, err := io.ReadAll(res.Body)
if err != nil {
log.Fatalln(err)
}
var loadedNotes []note
json.Unmarshal(data, &loadedNotes)
notes = loadedNotes
}
func edit(s tcell.Screen, text string, ext string) (string, error) { func edit(s tcell.Screen, text string, ext string) (string, error) {
cmd := exec.Command("vipe", "--suffix", ext) cmd := exec.Command("vipe", "--suffix", ext)
cmd.Env = append(cmd.Env, "EDITOR="+editor) cmd.Env = append(cmd.Env, "EDITOR="+editor)
@ -111,31 +53,6 @@ func edit(s tcell.Screen, text string, ext string) (string, error) {
return text, nil return text, nil
} }
func putNote(n note) error {
json, err := json.Marshal(n)
if err != nil {
return err
}
req, err := http.NewRequest("PUT", endpoint+"/notes/"+fmt.Sprint(n.Id), bytes.NewReader(json))
if err != nil {
return err
}
req.Header.Add("Content-Type", "application/json")
res, err := http.DefaultClient.Do(req)
if err != nil {
return err
}
if res.StatusCode != 200 {
return errors.New("Request failed: " + res.Status)
}
return nil
}
func editNote(s tcell.Screen, n note) (note, error) { func editNote(s tcell.Screen, n note) (note, error) {
newContent, err := edit(s, n.Content, "md") newContent, err := edit(s, n.Content, "md")
if err != nil { if err != nil {
@ -183,8 +100,65 @@ func editNoteTitle(s tcell.Screen, n note) (note, error) {
return n, nil return n, nil
} }
func emitStr(s tcell.Screen, x int, y int, style tcell.Style, str string) {
for _, c := range str {
var comb []rune
w := runewidth.RuneWidth(c)
if w == 0 {
comb = []rune{c}
c = ' '
w = 1
}
s.SetContent(x, y, c, comb, style)
x += w
}
}
func showNotes(s tcell.Screen) {
uiState = uiMain
s.Fill(' ', tcell.StyleDefault)
for idx, note := range notes {
style := tcell.StyleDefault
if idx == selectedNote {
style = style.Foreground(tcell.ColorBlack).Background(tcell.ColorWhite)
}
text := fmt.Sprint(idx) + ". " + note.Title
emitStr(s, 1, 1+idx*lineHeight, style, text)
}
_, h := s.Size()
emitStr(s, 1, h-1, tcell.StyleDefault.Foreground(tcell.ColorBlue), "n - new, r - rename, d - delete, Enter - edit, Esc - exit")
}
func showDelete(s tcell.Screen) {
uiState = uiDelete
s.Fill(' ', tcell.StyleDefault)
emitStr(s, 1, 1, tcell.StyleDefault, "Do you really want to delete the note '"+notes[selectedNote].Title+"'?")
emitStr(s, 1, 3, tcell.StyleDefault.Foreground(tcell.ColorBlue), "y - yes, n - no")
}
func showUI(s tcell.Screen) {
switch uiState {
case uiMain:
showNotes(s)
case uiDelete:
showDelete(s)
}
}
func main() { func main() {
loadNotes() var err error
notes, err = loadNotes()
if err != nil {
log.Fatalln(err)
}
s, err := tcell.NewScreen() s, err := tcell.NewScreen()
if err != nil { if err != nil {
@ -203,9 +177,11 @@ func main() {
for { for {
switch ev := s.PollEvent().(type) { switch ev := s.PollEvent().(type) {
case *tcell.EventResize: case *tcell.EventResize:
showNotes(s) showUI(s)
s.Sync() s.Sync()
case *tcell.EventKey: case *tcell.EventKey:
switch uiState {
case uiMain:
switch ev.Key() { switch ev.Key() {
case tcell.KeyUp: case tcell.KeyUp:
if selectedNote > 0 { if selectedNote > 0 {
@ -232,8 +208,13 @@ func main() {
notes[selectedNote] = n notes[selectedNote] = n
case tcell.KeyEscape:
s.Fini()
os.Exit(1)
case tcell.KeyRune: case tcell.KeyRune:
if ev.Rune() == 'r' { switch ev.Rune() {
case 'r':
n, err := editNoteTitle(s, notes[selectedNote]) n, err := editNoteTitle(s, notes[selectedNote])
if err != nil { if err != nil {
s.Fini() s.Fini()
@ -244,11 +225,63 @@ func main() {
showNotes(s) showNotes(s)
s.Sync() s.Sync()
case 'n':
title, err := edit(s, "", "txt")
if err != nil {
s.Fini()
log.Fatalln(err)
} }
case tcell.KeyEscape: if len(title) == 0 {
showNotes(s)
s.Sync()
break
}
n2, err := postNote(note{Title: title})
if err != nil {
s.Fini() s.Fini()
os.Exit(1) log.Fatalln(err)
}
notes = append(notes, n2)
selectedNote = len(notes) - 1
showNotes(s)
s.Sync()
case 'd':
showDelete(s)
s.Show()
}
}
case uiDelete:
switch ev.Key() {
case tcell.KeyRune:
switch ev.Rune() {
case 'y':
err := deleteNote(notes[selectedNote])
if err != nil {
s.Fini()
log.Fatalln(err)
}
notes = append(notes[:selectedNote], notes[selectedNote+1:]...)
if selectedNote >= len(notes) {
selectedNote = len(notes) - 1
}
showNotes(s)
s.Show()
case 'n':
showNotes(s)
s.Show()
}
}
} }
} }
} }