restructure and email validation

This commit is contained in:
GitBluub
2024-05-05 15:12:06 +02:00
parent 0ad421b10c
commit 8baaf5bae0
11 changed files with 370 additions and 113 deletions
+1
View File
@@ -0,0 +1 @@
vex.log
+5 -4
View File
@@ -9,6 +9,7 @@ import (
"net/http"
tea "github.com/charmbracelet/bubbletea"
"github.com/zoryia/vex/tui/models"
)
type statusMsg int
@@ -133,7 +134,7 @@ func register(username string, password string, email string) tea.Cmd {
}
}
type getEntriesSuccessMsg []Entry
type getEntriesSuccessMsg []models.Entry
func getEntries(jwt *string) tea.Cmd {
return func() tea.Msg {
@@ -148,7 +149,7 @@ func getEntries(jwt *string) tea.Cmd {
if err != nil {
return httpErrorMsg(err)
}
var entries []Entry
var entries []models.Entry
err = json.Unmarshal(data, &entries)
if err != nil {
return httpErrorMsg(err)
@@ -157,7 +158,7 @@ func getEntries(jwt *string) tea.Cmd {
}
}
type getFeedsSuccessMsg []Feed
type getFeedsSuccessMsg []models.Feed
func getFeeds(jwt *string) tea.Cmd {
return func() tea.Msg {
@@ -171,7 +172,7 @@ func getFeeds(jwt *string) tea.Cmd {
if err != nil {
return httpErrorMsg(err)
}
var feeds []Feed
var feeds []models.Feed
err = json.Unmarshal(data, &feeds)
if err != nil {
return httpErrorMsg(err)
+68 -51
View File
@@ -1,8 +1,8 @@
package main
import (
"fmt"
"net/http"
"log"
_ "log"
"os"
"strings"
"time"
@@ -10,32 +10,27 @@ import (
"github.com/charmbracelet/bubbles/key"
"github.com/charmbracelet/bubbles/list"
"github.com/charmbracelet/bubbles/textinput"
"github.com/charmbracelet/bubbles/viewport"
tea "github.com/charmbracelet/bubbletea"
huh "github.com/charmbracelet/huh"
. "github.com/zoryia/vex/tui/models"
. "github.com/zoryia/vex/tui/pages"
"github.com/zoryia/vex/tui/pages/auth"
"github.com/zoryia/vex/tui/pages/preview"
)
func (e Entry) FilterValue() string {
return e.ArticleTitle
}
func (e Entry) Title() string {
return e.ArticleTitle
}
func (e Entry) Description() string {
return fmt.Sprintf("%s", "my desc") // TODO: real description
}
type Model struct {
list list.Model
textInput textinput.Model
err error
auth Auth
auth auth.Model
page VexPage
query string
feeds []Feed
entries []Entry
tags []string
keys *ListKeyMap
Preview preview.Model
}
func New() *Model {
@@ -44,12 +39,12 @@ func New() *Model {
ti.Focus()
ti.CharLimit = 156
ti.Width = 56
return &Model{textInput: ti, auth: Auth{loginForm: getLoginForm(), registerForm: getRegisterForm(), jwt: new(string)}, page: LOGIN}
return &Model{textInput: ti, auth: auth.New(), page: ENTRIES, keys: NewListKeyMap(), Preview: preview.Model{Viewport: viewport.New(0, 0)}}
}
func (m Model) getEverything() tea.Cmd {
return func() tea.Msg {
return tea.Batch(getEntries(m.auth.jwt)) // getTags, getFeeds)
return tea.Batch(getEntries(m.auth.Jwt)) // getTags, getFeeds)
}
}
@@ -59,14 +54,14 @@ func (m *Model) initList(width int, height int) {
m.list.SetFilteringEnabled(false)
var f = Feed{Id: "1", Tags: []string{"Devops", "Kubernetes"}, Name: "zwindler", Url: "zwindler.blog", FaviconUrl: "zwindler.blog.favicon"}
m.list.SetItems([]list.Item{
Entry{Id: "1", ArticleTitle: "yay", Content: "ouin ouin ouin", Link: "awd", Date: time.Now(), IsRead: false, IsIgnored: false, IsReadLater: false, IsBookmarked: false, Feed: f},
Entry{Id: "1", ArticleTitle: "yay", Content: "ouin ouin ouinouin ouin ouinouin ouin ouinouin ouin ouinouin ouin ouinouin ouin ouinouin ouin ouinouin ouin ouinouin ouin ouinouin ouin ouin", Link: "awd", Date: time.Now(), IsRead: false, IsIgnored: false, IsReadLater: false, IsBookmarked: false, Feed: f},
Entry{Id: "2", ArticleTitle: "grrrrr", Content: "ouin ouin ouin", Link: "awd", Date: time.Now(), IsRead: false, IsIgnored: false, IsReadLater: false, IsBookmarked: false, Feed: f},
Entry{Id: "3", ArticleTitle: "my life is pain", Content: "ouin ouin ouin", Link: "awd", Date: time.Now(), IsRead: false, IsIgnored: false, IsReadLater: false, IsBookmarked: false, Feed: f},
})
}
func (m Model) Init() tea.Cmd {
return tea.Batch(checkServer, m.auth.loginForm.Init(), m.auth.registerForm.Init(), checkJwt(m.auth.jwt))
return tea.Batch(m.auth.LoginForm.Init(), m.auth.RegisterForm.Init(), checkJwt(m.auth.Jwt))
}
@@ -152,38 +147,26 @@ func (m *Model) deleteWordBackward() {
}
}
const url = "localhost:3000"
func checkServer() tea.Msg {
c := &http.Client{
Timeout: 10 * time.Second,
}
res, err := c.Get(url)
if err != nil {
return errMsg{err}
}
defer res.Body.Close() // nolint:errcheck
return statusMsg(res.StatusCode)
}
func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
var cmds []tea.Cmd
switch msg := msg.(type) {
case tea.WindowSizeMsg:
m.initList(msg.Width, msg.Height)
m.Preview.Viewport.Width = msg.Width
m.Preview.Viewport.Height = msg.Height - m.Preview.VerticalMarginHeight()
case invalidJwtMsg:
m.auth.jwt = new(string)
m.auth.Jwt = new(string)
m.page = LOGIN
return m, nil
case loginSuccessMsg:
*m.auth.jwt = msg.string
*m.auth.Jwt = msg.string
m.page = FEEDS
return m, m.getEverything()
case registerSuccessMsg:
*m.auth.jwt = msg.string
*m.auth.Jwt = msg.string
m.page = FEEDS
return m, m.getEverything()
@@ -205,22 +188,50 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
if len(words) > 0 && (strings.HasPrefix(words[len(words)-1], "tag:") || strings.HasPrefix(words[len(words)-1], "feed:")) {
m.deleteWordBackward()
}
case key.Matches(msg, m.keys.IgnoreToggle) && m.page == "FEEDS":
// TODO: ignore the post
return m, nil
case key.Matches(msg, m.keys.ReadToggle):
// TODO: mark as read
return m, nil
case key.Matches(msg, m.keys.ReadLaterToggle):
// TODO: add to read later
return m, nil
case key.Matches(msg, m.keys.BookmarkToggle):
// TODO: toggle bookmark
return m, nil
case key.Matches(msg, m.keys.Query):
// TODO: launch query input
return m, nil
case key.Matches(msg, m.keys.PreviewPost):
var e = m.list.SelectedItem()
entry := e.(Entry)
m.Preview.Entry = entry
m.Preview.Viewport.SetContent(entry.Content)
m.page = PREVIEW
log.Print(entry.Content)
log.Print(m.Preview.Viewport.VisibleLineCount())
}
}
var cmds []tea.Cmd
// Process the form
// LOGIN
if m.page == LOGIN {
form, cmd := m.auth.loginForm.Update(msg)
form, cmd := m.auth.LoginForm.Update(msg)
if f, ok := form.(*huh.Form); ok {
m.auth.loginForm = f
m.auth.LoginForm = f
cmds = append(cmds, cmd)
}
if m.auth.loginForm.State == huh.StateCompleted {
username := m.auth.loginForm.GetString("email")
password := m.auth.loginForm.GetString("password")
if m.auth.LoginForm.State == huh.StateCompleted {
username := m.auth.LoginForm.GetString("email")
password := m.auth.LoginForm.GetString("password")
cmds = append(cmds, login(username, password))
}
}
@@ -229,21 +240,24 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
// Process the form
// LOGIN
registerForm, cmd := m.auth.registerForm.Update(msg)
registerForm, cmd := m.auth.RegisterForm.Update(msg)
if f, ok := registerForm.(*huh.Form); ok {
m.auth.registerForm = f
m.auth.RegisterForm = f
cmds = append(cmds, cmd)
}
if m.auth.registerForm.State == huh.StateCompleted {
username := m.auth.registerForm.GetString("username")
password := m.auth.registerForm.GetString("password")
email := m.auth.registerForm.GetString("email")
if m.auth.RegisterForm.State == huh.StateCompleted {
username := m.auth.RegisterForm.GetString("username")
password := m.auth.RegisterForm.GetString("password")
email := m.auth.RegisterForm.GetString("email")
cmds = append(cmds, register(username, password, email))
}
}
var cmd tea.Cmd
m.Preview.Viewport, cmd = m.Preview.Viewport.Update(msg)
cmds = append(cmds, cmd)
m.list, cmd = m.list.Update(msg)
cmds = append(cmds, cmd)
m.textInput, cmd = m.textInput.Update(msg)
@@ -261,7 +275,10 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
func main() {
tea.LogToFile("vex.log", "")
m := New()
p := tea.NewProgram(m)
p := tea.NewProgram(m,
tea.WithAltScreen(), // use the full size of the terminal in its "alternate screen buffer"
tea.WithMouseCellMotion(),
)
if _, err := p.Run(); err != nil {
os.Exit(1)
}
+11 -18
View File
@@ -4,30 +4,21 @@ import (
"fmt"
"github.com/charmbracelet/lipgloss"
. "github.com/zoryia/vex/tui/pages"
)
const (
LOGIN = "LOGIN"
REGISTER = "REGISTER"
ENTRIES = "ENTRIES"
FEEDS = "FEEDS"
TAGS = "TAGS"
)
type VexPage string
func (m Model) LoginView() string {
func (m Model) AuthView() string {
return lipgloss.JoinHorizontal(
lipgloss.Left,
m.auth.loginForm.View(),
m.auth.registerForm.View(),
m.auth.LoginForm.View(),
m.auth.RegisterForm.View(),
)
}
func (m Model) EntriesView() string {
return ""
return m.list.View()
}
func (m Model) FeedsView() string {
return fmt.Sprintf("%s ", *m.auth.jwt)
return fmt.Sprintf("%s ", *m.auth.Jwt)
}
func (m Model) TagsView() string {
return ""
@@ -35,15 +26,17 @@ func (m Model) TagsView() string {
func (m Model) View() string {
switch m.page {
case LOGIN:
return m.LoginView()
return m.AuthView()
case REGISTER:
return m.LoginView()
return m.AuthView()
case ENTRIES:
return m.EntriesView()
case FEEDS:
return m.FeedsView()
case TAGS:
return m.TagsView()
case PREVIEW:
return m.Preview.View()
}
return m.textInput.View() + m.list.View()
return "Really unexpected state, get help"
}
-28
View File
@@ -1,28 +0,0 @@
package main
import (
"time"
)
type Feed struct {
Id string `json:"id"`
Name string `json:"name"`
Url string `json:"url"`
FaviconUrl string `json:"faviconUrl"`
Tags []string `json:"tags"`
}
type Entry struct {
Id string `json:"id"`
ArticleTitle string `json:"title"`
Content string `json:"content"`
Link string `json:"link"`
Date time.Time `json:"time"`
Author *string `json:"author"` // author not always specified
IsRead bool `json:"isRead"`
IsBookmarked bool `json:"IsBookmarked"`
IsIgnored bool `json:"isIgnored"`
IsReadLater bool `json:"isReadLater"`
Feed Feed `json:"feed"`
}
+20 -3
View File
@@ -1,31 +1,48 @@
module vex.tui
module github.com/zoryia/vex/tui
go 1.22.2
require (
github.com/charmbracelet/bubbles v0.18.0
github.com/charmbracelet/bubbletea v0.26.1
github.com/charmbracelet/glamour v0.7.0
github.com/charmbracelet/huh v0.3.0
github.com/charmbracelet/lipgloss v0.10.0
github.com/go-playground/validator/v10 v10.20.0
)
require (
github.com/alecthomas/chroma/v2 v2.8.0 // indirect
github.com/atotto/clipboard v0.1.4 // indirect
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
github.com/aymerick/douceur v0.2.0 // indirect
github.com/badoux/checkmail v1.2.4 // indirect
github.com/catppuccin/go v0.2.0 // indirect
github.com/charmbracelet/lipgloss v0.10.0 // indirect
github.com/dlclark/regexp2 v1.4.0 // indirect
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/gorilla/css v1.0.0 // indirect
github.com/leodido/go-urn v1.4.0 // indirect
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-localereader v0.0.1 // indirect
github.com/mattn/go-runewidth v0.0.15 // indirect
github.com/microcosm-cc/bluemonday v1.0.25 // indirect
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect
github.com/muesli/cancelreader v0.2.2 // indirect
github.com/muesli/reflow v0.3.0 // indirect
github.com/muesli/termenv v0.15.2 // indirect
github.com/olekukonko/tablewriter v0.0.5 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/sahilm/fuzzy v0.1.1-0.20230530133925-c48e322e2a8f // indirect
github.com/yuin/goldmark v1.5.4 // indirect
github.com/yuin/goldmark-emoji v1.0.2 // indirect
golang.org/x/crypto v0.19.0 // indirect
golang.org/x/net v0.21.0 // indirect
golang.org/x/sync v0.7.0 // indirect
golang.org/x/sys v0.19.0 // indirect
golang.org/x/term v0.19.0 // indirect
golang.org/x/text v0.13.0 // indirect
golang.org/x/text v0.14.0 // indirect
)
+54 -2
View File
@@ -1,30 +1,65 @@
github.com/alecthomas/assert/v2 v2.2.1 h1:XivOgYcduV98QCahG8T5XTezV5bylXe+lBxLG2K2ink=
github.com/alecthomas/assert/v2 v2.2.1/go.mod h1:pXcQ2Asjp247dahGEmsZ6ru0UVwnkhktn7S0bBDLxvQ=
github.com/alecthomas/chroma/v2 v2.8.0 h1:w9WJUjFFmHHB2e8mRpL9jjy3alYDlU0QLDezj1xE264=
github.com/alecthomas/chroma/v2 v2.8.0/go.mod h1:yrkMI9807G1ROx13fhe1v6PN2DDeaR73L3d+1nmYQtw=
github.com/alecthomas/repr v0.2.0 h1:HAzS41CIzNW5syS8Mf9UwXhNH1J9aix/BvDRf1Ml2Yk=
github.com/alecthomas/repr v0.2.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4=
github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
github.com/badoux/checkmail v1.2.4 h1:4zMjdYDjE2Q7xF06VNfyN8P9JGU7epLjNb+Yu5OThVI=
github.com/badoux/checkmail v1.2.4/go.mod h1:XroCOBU5zzZJcLvgwU15I+2xXyCdTWXyR9MGfRhBYy0=
github.com/catppuccin/go v0.2.0 h1:ktBeIrIP42b/8FGiScP9sgrWOss3lw0Z5SktRoithGA=
github.com/catppuccin/go v0.2.0/go.mod h1:8IHJuMGaUUjQM82qBrGNBv7LFq6JI3NnQCF6MOlZjpc=
github.com/charmbracelet/bubbles v0.18.0 h1:PYv1A036luoBGroX6VWjQIE9Syf2Wby2oOl/39KLfy0=
github.com/charmbracelet/bubbles v0.18.0/go.mod h1:08qhZhtIwzgrtBjAcJnij1t1H0ZRjwHyGsy6AL11PSw=
github.com/charmbracelet/bubbletea v0.26.1 h1:xujcQeF73rh4jwu3+zhfQsvV18x+7zIjlw7/CYbzGJ0=
github.com/charmbracelet/bubbletea v0.26.1/go.mod h1:FzKr7sKoO8iFVcdIBM9J0sJOcQv5nDQaYwsee3kpbgo=
github.com/charmbracelet/glamour v0.7.0 h1:2BtKGZ4iVJCDfMF229EzbeR1QRKLWztO9dMtjmqZSng=
github.com/charmbracelet/glamour v0.7.0/go.mod h1:jUMh5MeihljJPQbJ/wf4ldw2+yBP59+ctV36jASy7ps=
github.com/charmbracelet/huh v0.3.0 h1:CxPplWkgW2yUTDDG0Z4S5HH8SJOosWHd4LxCvi0XsKE=
github.com/charmbracelet/huh v0.3.0/go.mod h1:fujUdKX8tC45CCSaRQdw789O6uaCRwx8l2NDyKfC4jA=
github.com/charmbracelet/lipgloss v0.10.0 h1:KWeXFSexGcfahHX+54URiZGkBFazf70JNMtwg/AFW3s=
github.com/charmbracelet/lipgloss v0.10.0/go.mod h1:Wig9DSfvANsxqkRsqj6x87irdy123SR4dOXlKa91ciE=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dlclark/regexp2 v1.4.0 h1:F1rxgk7p4uKjwIQxBs9oAXe5CqrXlCduYEJvrF4u93E=
github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4=
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM=
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.20.0 h1:K9ISHbSaI0lyB2eWMPJo+kOS/FBExVwjEviJTixqxL8=
github.com/go-playground/validator/v10 v10.20.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY=
github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c=
github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4=
github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/microcosm-cc/bluemonday v1.0.25 h1:4NEwSfiJ+Wva0VxN5B8OwMicaJvD8r9tlJWm9rtloEg=
github.com/microcosm-cc/bluemonday v1.0.25/go.mod h1:ZIOjCQp1OrzBBPIJmfX4qDYFuhU02nx4bn030ixfHLE=
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI=
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo=
github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
@@ -33,12 +68,27 @@ github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s=
github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8=
github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo=
github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8=
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/sahilm/fuzzy v0.1.1-0.20230530133925-c48e322e2a8f h1:MvTmaQdww/z0Q4wrYjDSCcZ78NoftLQyHBSLW/Cx79Y=
github.com/sahilm/fuzzy v0.1.1-0.20230530133925-c48e322e2a8f/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/yuin/goldmark v1.3.7/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.5.4 h1:2uY/xC0roWy8IBEGLgB1ywIoEJFGmRrX21YQcvGZzjU=
github.com/yuin/goldmark v1.5.4/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/yuin/goldmark-emoji v1.0.2 h1:c/RgTShNgHTtc6xdz2KKI74jJr6rWi7FPgnP9GAsO5s=
github.com/yuin/goldmark-emoji v1.0.2/go.mod h1:RhP/RWpexdp+KHs7ghKnifRoIs/Bq4nDS7tRbCkOwKY=
golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo=
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@@ -47,5 +97,7 @@ golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q=
golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk=
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+83
View File
@@ -0,0 +1,83 @@
package models
import (
"fmt"
"time"
"github.com/charmbracelet/bubbles/key"
)
type Feed struct {
Id string `json:"id"`
Name string `json:"name"`
Url string `json:"url"`
FaviconUrl string `json:"faviconUrl"`
Tags []string `json:"tags"`
}
type Entry struct {
Id string `json:"id"`
ArticleTitle string `json:"title"`
Content string `json:"content"`
Link string `json:"link"`
Date time.Time `json:"time"`
Author *string `json:"author"` // author not always specified
IsRead bool `json:"isRead"`
IsBookmarked bool `json:"IsBookmarked"`
IsIgnored bool `json:"isIgnored"`
IsReadLater bool `json:"isReadLater"`
Feed Feed `json:"feed"`
}
func (e Entry) FilterValue() string {
return e.ArticleTitle
}
func (e Entry) Title() string {
return e.ArticleTitle
}
func (e Entry) Description() string {
return fmt.Sprintf("%s", "my desc") // TODO: real description (tags and author + date ?)
}
type ListKeyMap struct {
Query key.Binding
BookmarkToggle key.Binding
ReadToggle key.Binding
ReadLaterToggle key.Binding
IgnoreToggle key.Binding
PreviewPost key.Binding
}
func NewListKeyMap() *ListKeyMap {
return &ListKeyMap{
PreviewPost: key.NewBinding(
key.WithKeys("enter"),
key.WithHelp("enter", "preview post"),
),
Query: key.NewBinding(
key.WithKeys("/"),
key.WithHelp("/", "query posts"),
),
BookmarkToggle: key.NewBinding(
key.WithKeys("b"),
key.WithHelp("b", "toggle bookmarked"),
),
ReadToggle: key.NewBinding(
key.WithKeys("r"),
key.WithHelp("r", "toggle mark as read"),
),
IgnoreToggle: key.NewBinding(
key.WithKeys("x", "d"),
key.WithHelp("x", "ignore post"),
key.WithHelp("d", "ignore post"),
),
ReadLaterToggle: key.NewBinding(
key.WithKeys("m"),
key.WithHelp("m", "add to read later"),
),
}
}
+27 -7
View File
@@ -1,13 +1,14 @@
package main
package auth
import (
"github.com/badoux/checkmail"
huh "github.com/charmbracelet/huh"
)
type Auth struct {
loginForm *huh.Form
registerForm *huh.Form
jwt *string
type Model struct {
LoginForm *huh.Form
RegisterForm *huh.Form
Jwt *string
}
func getLoginForm() *huh.Form {
@@ -15,7 +16,14 @@ func getLoginForm() *huh.Form {
huh.NewGroup(
huh.NewInput().
Title("Email").
Key("email"),
Key("email").Validate(
func(s string) error {
err := checkmail.ValidateFormat(s)
if err != nil {
return err
}
return nil
}),
huh.NewInput().
Title("Password").
Key("password").
@@ -28,7 +36,14 @@ func getRegisterForm() *huh.Form {
huh.NewGroup(
huh.NewInput().
Title("Email").
Key("email"),
Key("email").Validate(
func(s string) error {
err := checkmail.ValidateFormat(s)
if err != nil {
return err
}
return nil
}),
huh.NewInput().
Title("Username").
Key("username"),
@@ -42,3 +57,8 @@ func getRegisterForm() *huh.Form {
Password(true),
)).WithWidth(40)
}
func New() Model {
return Model{RegisterForm: getRegisterForm(), LoginForm: getLoginForm(), Jwt: new(string)}
}
+13
View File
@@ -0,0 +1,13 @@
package pages
const (
LOGIN = "LOGIN"
REGISTER = "REGISTER"
ENTRIES = "ENTRIES"
FEEDS = "FEEDS"
TAGS = "TAGS"
IGNORED = "IGNORED"
PREVIEW = "PREVIEW"
)
type VexPage string
+88
View File
@@ -0,0 +1,88 @@
package preview
import (
"fmt"
"strings"
"github.com/charmbracelet/bubbles/viewport"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/glamour"
"github.com/charmbracelet/lipgloss"
"github.com/zoryia/vex/tui/models"
"errors"
)
type Model struct {
Entry models.Entry
Viewport viewport.Model
}
// RenderMarkdown renders the markdown content with glamour.
func RenderMarkdown(width int, content string) (string, error) {
background := "light"
if lipgloss.HasDarkBackground() {
background = "dark"
}
r, _ := glamour.NewTermRenderer(
glamour.WithWordWrap(width),
glamour.WithStandardStyle(background),
)
out, err := r.Render(content)
if err != nil {
return "", errors.Unwrap(err)
}
return out, nil
}
func renderMarkdownCmd(width int, entry models.Entry) tea.Cmd {
return func() tea.Msg {
_, err := RenderMarkdown(width, entry.Content)
if err != nil {
//return errorMsg(err)
}
return nil
// return renderMarkdownMsg(markdownContent)
}
}
var (
titleStyle = func() lipgloss.Style {
b := lipgloss.RoundedBorder()
b.Right = "├"
return lipgloss.NewStyle().BorderStyle(b).Padding(0, 1)
}()
infoStyle = func() lipgloss.Style {
b := lipgloss.RoundedBorder()
b.Left = "┤"
return titleStyle.Copy().BorderStyle(b)
}()
)
// TODO: render as markdown with glamour
func (m Model) View() string {
return fmt.Sprintf("%s\n%s\n%s", m.headerView(), m.Viewport.View(), m.footerView())
}
func (m Model) VerticalMarginHeight() int {
headerHeight := lipgloss.Height(m.headerView())
footerHeight := lipgloss.Height(m.footerView())
return headerHeight + footerHeight
}
func (m Model) headerView() string {
title := titleStyle.Render(m.Entry.ArticleTitle)
line := strings.Repeat("─", max(0, m.Viewport.Width-lipgloss.Width(title)))
return lipgloss.JoinHorizontal(lipgloss.Center, title, line)
}
func (m Model) footerView() string {
info := infoStyle.Render(fmt.Sprintf("%3.f%%", m.Viewport.ScrollPercent()*100))
line := strings.Repeat("─", max(0, m.Viewport.Width-lipgloss.Width(info)))
return lipgloss.JoinHorizontal(lipgloss.Center, line, info)
}