Skip to content

Commit

Permalink
Merge pull request #5 from isabelroses/state-tracking
Browse files Browse the repository at this point in the history
  • Loading branch information
isabelroses authored Apr 18, 2024
2 parents 84ec2bc + 6e0c377 commit 7496d7d
Show file tree
Hide file tree
Showing 9 changed files with 164 additions and 27 deletions.
Binary file modified .github/assets/demo.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
12 changes: 8 additions & 4 deletions .github/assets/demo.tape
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ Output demo.gif

Require echo

Set Shell "bash"
Set Shell "fish"
Set Width 1920
Set Height 1080
Set CursorBlink false
Expand All @@ -17,11 +17,15 @@ Enter
Sleep 500ms
Type "?"
Sleep 500ms
Down
Down 1
Sleep 500ms
Enter
Sleep 500ms
Down 4
Type "x"
Sleep 1s
Enter
Sleep 1.5s
Down 10
Sleep 2s
Down 15
Type "q"
Sleep 5s
47 changes: 42 additions & 5 deletions cmd/keys.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ type keyMap struct {
Refresh key.Binding
RefreshAll key.Binding
Search key.Binding
ToggleRead key.Binding
ReadAll key.Binding
}

func (k keyMap) ShortHelp() []key.Binding {
Expand All @@ -31,6 +33,7 @@ func (k keyMap) FullHelp() [][]key.Binding {
{k.Help, k.Quit},
{k.Refresh, k.RefreshAll},
{k.Open, k.Search},
{k.ToggleRead, k.ReadAll},
}
}

Expand Down Expand Up @@ -67,6 +70,14 @@ var keys = keyMap{
key.WithKeys("/"),
key.WithHelp("/", "search"),
),
ToggleRead: key.NewBinding(
key.WithKeys("x"),
key.WithHelp("x", "toggle read"),
),
ReadAll: key.NewBinding(
key.WithKeys("X"),
key.WithHelp("X", "toggle read"),
),
}

func (m model) handleKeys(msg tea.KeyMsg) (model, tea.Cmd) {
Expand All @@ -78,17 +89,20 @@ func (m model) handleKeys(msg tea.KeyMsg) (model, tea.Cmd) {
case key.Matches(msg, m.keys.Quit):
switch m.context {
case "reader":
m.context = "content"
m.viewport.SetYOffset(0)
m = m.loadContent(m.feed.ID)
m.table.SetCursor(m.post.ID)
case "content":
m = m.loadHome()
m.table.SetCursor(m.feed.ID)
case "search":
m = m.loadContent()
m = m.loadContent(m.table.Cursor())
m.table.Focus()
m.filter.Blur()
default:
err := m.feeds.WriteTracking()
if err != nil {
log.Fatalf("Could not write tracking data: %s", err)
}
return m, tea.Quit
}

Expand All @@ -107,7 +121,7 @@ func (m model) handleKeys(msg tea.KeyMsg) (model, tea.Cmd) {
case "content":
feed := &m.feed
feed.Posts = lib.GetPosts(feed.URL)
m = m.loadContent()
m = m.loadContent(m.feed.ID)
}

case key.Matches(msg, m.keys.RefreshAll):
Expand All @@ -134,14 +148,37 @@ func (m model) handleKeys(msg tea.KeyMsg) (model, tea.Cmd) {
}

default:
m = m.loadContent()
m = m.loadContent(m.table.Cursor())
m.table.SetCursor(0)
}

case key.Matches(msg, m.keys.Search):
if m.context != "search" {
m = m.loadSearch()
}

case key.Matches(msg, m.keys.ToggleRead):
switch m.context {
case "reader":
lib.ToggleRead(m.feeds, m.feed.ID, m.post.ID)
m = m.loadContent(m.feed.ID)
case "content":
lib.ToggleRead(m.feeds, m.feed.ID, m.table.Cursor())
m = m.loadContent(m.feed.ID)
}

case key.Matches(msg, m.keys.ReadAll):
switch m.context {
case "reader":
// if we are in the reader view, fall back to the normal mark all as read
lib.ToggleRead(m.feeds, m.feed.ID, m.post.ID)
case "content":
lib.ReadAll(m.feeds, m.feed.ID)
m = m.loadContent(m.table.Cursor())
case "home":
lib.ReadAll(m.feeds, m.table.Cursor())
m = m.loadHome()
}
}

return m, nil
Expand Down
23 changes: 15 additions & 8 deletions cmd/load.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package cmd

import (
"strconv"
"strings"

"github.com/charmbracelet/bubbles/table"
Expand All @@ -11,12 +12,14 @@ import (
// load the home view, this conists of the list of feeds
func (m model) loadHome() model {
columns := []table.Column{
{Title: "Title", Width: m.table.Width()},
{Title: "Unread", Width: 7},
{Title: "Title", Width: m.table.Width() - 7},
}

rows := []table.Row{}
for _, Feeds := range m.feeds {
rows = append(rows, table.Row{Feeds.Title})
for _, Feed := range m.feeds {
totalUnread := strconv.Itoa(Feed.GetTotalUnreads())
rows = append(rows, table.Row{totalUnread, Feed.Title})
}

m = m.loadNewTable(columns, rows)
Expand All @@ -25,19 +28,23 @@ func (m model) loadHome() model {
return m
}

func (m model) loadContent() model {
id := m.table.Cursor()
func (m model) loadContent(id int) model {
feed := m.feeds[id]
feed.ID = id

columns := []table.Column{
{Title: "Date", Width: 13},
{Title: "Title", Width: m.table.Width() - 15},
{Title: "Date", Width: 11},
{Title: "Unread", Width: 7},
{Title: "Title", Width: m.table.Width() - 27},
}

rows := []table.Row{}
for _, post := range feed.Posts {
rows = append(rows, table.Row{post.Date, post.Title})
unread := "x"
if !post.Read {
unread = "✓"
}
rows = append(rows, table.Row{post.Date, unread, post.Title})
}

m = m.loadNewTable(columns, rows)
Expand Down
11 changes: 11 additions & 0 deletions cmd/main.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package cmd

import (
"log"

"github.com/charmbracelet/bubbles/viewport"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
Expand Down Expand Up @@ -40,7 +42,12 @@ func (m model) handleWindowSize(msg tea.WindowSizeMsg) model {
if !m.ready {
m.feeds = lib.GetAllContent(true)
m.viewport = viewport.New(width, height)
err := error(nil)
m.feeds, err = m.feeds.ReadTracking()
m = m.loadHome()
if err != nil {
log.Fatal(err)
}
m.ready = true
} else {
m.viewport.Width = width
Expand Down Expand Up @@ -82,6 +89,10 @@ func (m model) updateViewport(msg tea.Msg) (model, tea.Cmd) {
m.viewport.SetContent(view)
}

if m.context == "reader" && m.viewport.ScrollPercent() >= 0.8 {
lib.MarkRead(m.feeds, m.feed.ID, m.post.ID)
}

m.viewport, cmd = m.viewport.Update(msg)
cmds = append(cmds, cmd)

Expand Down
30 changes: 21 additions & 9 deletions lib/feeds.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,20 @@ package lib
import "sort"

type Post struct {
Title string
Content string
Link string
Date string
ID int
UUID string `json:"uuid"`
Title string `json:"-"`
Content string `json:"-"`
Link string `json:"-"`
Date string `json:"-"`
ID int `json:"-"`
Read bool `json:"read"`
}

type Feed struct {
Title string
URL string
Posts []Post
ID int
Title string `json:"-"`
URL string `json:"URL"`
Posts []Post `json:"posts"`
ID int `json:"-"`
}

type Feeds []Feed
Expand All @@ -33,3 +35,13 @@ func (f Feeds) sort(urls []string) Feeds {

return f
}

func (f Feed) GetTotalUnreads() int {
total := 0
for _, post := range f.Posts {
if !post.Read {
total++
}
}
return total
}
1 change: 1 addition & 0 deletions lib/fetch.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ func createPost(item *gofeed.Item) Post {
Content: content,
Link: item.Link,
Date: ConvertDate(item.Published),
UUID: item.GUID,
}

return post
Expand Down
65 changes: 65 additions & 0 deletions lib/state.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package lib

import (
"encoding/json"
"log"
"os"

"github.com/adrg/xdg"
)

func ToggleRead(feeds Feeds, feedID int, postID int) Feeds {
postr := &feeds[feedID].Posts[postID]
postr.Read = !postr.Read
return feeds
}

func ReadAll(feeds Feeds, feedID int) Feeds {
for i := range feeds[feedID].Posts {
feeds[feedID].Posts[i].Read = true
}
return feeds
}

func MarkRead(feeds Feeds, feedID int, postID int) Feeds {
postr := &feeds[feedID].Posts[postID]
postr.Read = true
return feeds
}

func (feeds Feeds) WriteTracking() error {
json, err := json.Marshal(feeds)
if err != nil {
return err
}
return os.WriteFile(getSateFile(), json, 0644)
}

// Read from JSON file
func (feeds Feeds) ReadTracking() (Feeds, error) {
fileStr := getSateFile()
if _, err := os.Stat(fileStr); os.IsNotExist(err) {
err := feeds.WriteTracking()
if err != nil {
log.Fatalf("could not write tracking file: %v", err)
}
}

file, err := os.ReadFile(fileStr)
if err != nil {
return nil, err
}
err = json.Unmarshal(file, &feeds)
if err != nil {
return nil, err
}
return feeds, nil
}

func getSateFile() string {
stateFile, err := xdg.StateFile("izrss/tracking.json")
if err != nil {
log.Fatalf("could not find state file: %v", err)
}
return stateFile
}
2 changes: 1 addition & 1 deletion nix/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ buildGoModule {

src = ../.;

vendorHash = "sha256-u50qWuZH2VjnHWjCMeEYFKsVxQarNbx7ixZ+aJ8xOFw=";
vendorHash = "sha256-gH5AFroreBD0tQmT99Bmo2pAdPkiPWUNGsmKX4p3/JA=";

ldflags = [
"-s"
Expand Down

0 comments on commit 7496d7d

Please sign in to comment.