Create/Delete note, Clean up code
This commit is contained in:
parent
3ef0fbe76f
commit
e7d12dc1d7
105
api.go
Normal file
105
api.go
Normal 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
219
main.go
@ -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()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user