Skip to content

Commit 6db4488

Browse files
committed
Merge branch 'develop'
2 parents daa4f0b + 5276b31 commit 6db4488

File tree

10 files changed

+148
-86
lines changed

10 files changed

+148
-86
lines changed

pkg/ansi/codes.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package ansi
2+
3+
/*
4+
see: https://en.wikipedia.org/wiki/ANSI_escape_code
5+
*/
6+
7+
const (
8+
/* colors */
9+
RED = 31
10+
GREEN = 32
11+
WHITE = 37
12+
// display attributes
13+
ALL_ATTRIBUTES_OFF = 0
14+
REVERSE_COLOR = 7
15+
BOLD = 1
16+
DEFAULT_FG_COLOR = 39
17+
// erase
18+
ERASE_ENTIRE_SCREEN = 2
19+
//
20+
CURSOR = 25
21+
)

pkg/ansi/escape_sequences.go

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package ansi
2+
3+
import (
4+
"fmt"
5+
"strings"
6+
7+
"github.com/giulianopz/newscanoe/pkg/ascii"
8+
)
9+
10+
/*
11+
see: https://vt100.net/docs/vt100-ug/chapter3.html
12+
*/
13+
14+
const (
15+
16+
// control sequence introducer
17+
csi = string(ascii.ESC) + "["
18+
19+
MOVE_CURSOR_FMT = csi + "%d;%dH"
20+
SGR_FMT = csi + "%dm"
21+
ERASE_FMT = csi + "%dJ"
22+
SET_MODE_FMT = csi + "%sh"
23+
RESET_MODE = csi + "%sl"
24+
)
25+
26+
func MoveCursor(y, x int) string {
27+
return fmt.Sprintf(MOVE_CURSOR_FMT, y, x)
28+
}
29+
30+
// SGR sets display attributes
31+
// see: https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_(Select_Graphic_Rendition)_parameters
32+
func SGR(n int) string {
33+
return fmt.Sprintf(SGR_FMT, n)
34+
}
35+
36+
func Erase(n int) string {
37+
return fmt.Sprintf(ERASE_FMT, n)
38+
}
39+
40+
func SetMode(params ...string) string {
41+
return fmt.Sprintf(SET_MODE_FMT, strings.Join(params, ";"))
42+
}
43+
44+
func ResetMode(params ...string) string {
45+
return fmt.Sprintf(RESET_MODE, strings.Join(params, ";"))
46+
}
47+
48+
func ShowCursor() string {
49+
return SetMode(fmt.Sprintf("?%d", CURSOR))
50+
}
51+
52+
func HideCursor() string {
53+
return ResetMode(fmt.Sprintf("?%d", CURSOR))
54+
}

pkg/ascii/code.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
package ascii
22

33
/*
4-
see: https://theasciicode.com.ar/
4+
// ASCII control codes
5+
see:
6+
- https://theasciicode.com.ar/
7+
- https://en.wikipedia.org/wiki/C0_and_C1_control_codes
58
*/
69

710
const (

pkg/cache/cache.go

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,13 +36,15 @@ type Feed struct {
3636
Url string
3737
Title string
3838
Items []*Item
39+
New bool
3940
}
4041

4142
func NewFeed(url, title string) *Feed {
4243
return &Feed{
4344
Title: title,
4445
Url: url,
4546
Items: make([]*Item, 0),
47+
New: true,
4648
}
4749
}
4850

@@ -58,13 +60,15 @@ type Item struct {
5860
Title string
5961
Url string
6062
PubDate time.Time
63+
New bool
6164
}
6265

6366
func NewItem(title, url string, pubDate time.Time) *Item {
6467
return &Item{
6568
Title: title,
6669
Url: url,
6770
PubDate: pubDate,
71+
New: true,
6872
}
6973
}
7074

@@ -129,11 +133,19 @@ func (c *Cache) AddFeed(parsedFeed *gofeed.Feed, url string) error {
129133
if cachedFeed.Url == url {
130134

131135
cachedFeed.Title = title
132-
cachedFeed.Items = make([]*Item, 0)
136+
cachedFeed.New = true
137+
//cachedFeed.Items = make([]*Item, 0)
133138

134139
for _, parsedItem := range parsedFeed.Items {
135-
cachedItem := NewItem(parsedItem.Title, parsedItem.Link, *parsedItem.PublishedParsed)
136-
cachedFeed.Items = append(cachedFeed.Items, cachedItem)
140+
141+
alreadyPresent := slices.ContainsFunc(cachedFeed.Items, func(i *Item) bool {
142+
return i.Title == parsedItem.Title
143+
})
144+
145+
if !alreadyPresent {
146+
cachedItem := NewItem(parsedItem.Title, parsedItem.Link, *parsedItem.PublishedParsed)
147+
cachedFeed.Items = append(cachedFeed.Items, cachedItem)
148+
}
137149
}
138150
log.Default().Printf("refreshed cached feed with url: %s\n", url)
139151
return nil

pkg/display/display.go

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ import (
1010
"time"
1111
"unicode/utf8"
1212

13+
"github.com/giulianopz/newscanoe/pkg/ansi"
1314
"github.com/giulianopz/newscanoe/pkg/cache"
14-
"github.com/giulianopz/newscanoe/pkg/escape"
1515
"github.com/giulianopz/newscanoe/pkg/util"
1616
"github.com/mmcdole/gofeed"
1717
)
@@ -87,7 +87,7 @@ func New() *display {
8787
startoff: 0,
8888
endoff: 0,
8989
cache: cache.NewCache(),
90-
bottomBarColor: escape.WHITE,
90+
bottomBarColor: ansi.WHITE,
9191
ListenToKeyStroke: true,
9292
client: &http.Client{
9393
Timeout: 3 * time.Second,
@@ -106,7 +106,7 @@ func (d *display) setTmpBottomMessage(t time.Duration, msg string) {
106106
d.setBottomMessage(msg)
107107
go func() {
108108
time.AfterFunc(t, func() {
109-
d.bottomBarColor = escape.WHITE
109+
d.bottomBarColor = ansi.WHITE
110110
d.setBottomMessage(previous)
111111
})
112112
}()
@@ -124,9 +124,9 @@ func (d *display) Quit(quitC chan bool) {
124124

125125
d.ListenToKeyStroke = false
126126

127-
fmt.Fprint(os.Stdout, escape.SHOW_CURSOR)
128-
fmt.Fprint(os.Stdout, escape.ERASE_ENTIRE_SCREEN)
129-
fmt.Fprint(os.Stdout, escape.MoveCursor(1, 1))
127+
fmt.Fprint(os.Stdout, ansi.ShowCursor())
128+
fmt.Fprint(os.Stdout, ansi.Erase(ansi.ERASE_ENTIRE_SCREEN))
129+
fmt.Fprint(os.Stdout, ansi.MoveCursor(1, 1))
130130
quitC <- true
131131
}
132132

@@ -234,8 +234,8 @@ func (d *display) draw(buf *bytes.Buffer) {
234234
for i := d.startoff; i <= d.endoff; i++ {
235235

236236
if i == d.currentRow() && d.currentSection != ARTICLE_TEXT && !d.editingMode {
237-
buf.WriteString(escape.REVERSE_COLOR)
238-
buf.WriteString(escape.SelectGraphicRendition(escape.WHITE))
237+
buf.WriteString(ansi.SGR(ansi.REVERSE_COLOR))
238+
buf.WriteString(ansi.SGR(ansi.WHITE))
239239
}
240240

241241
// TODO check that the terminal supports Unicode output, before outputting a Unicode character
@@ -254,8 +254,8 @@ func (d *display) draw(buf *bytes.Buffer) {
254254
}
255255

256256
if i == d.currentRow() && d.currentSection != ARTICLE_TEXT {
257-
buf.WriteString(escape.ATTRIBUTES_OFF)
258-
buf.WriteString(escape.DEFAULT_FG_COLOR)
257+
buf.WriteString(ansi.SGR(ansi.ALL_ATTRIBUTES_OFF))
258+
buf.WriteString(ansi.SGR(ansi.DEFAULT_FG_COLOR))
259259
}
260260

261261
buf.WriteString("\r\n")
@@ -278,12 +278,12 @@ func (d *display) draw(buf *bytes.Buffer) {
278278
}
279279
padding := d.width - utf8.RuneCountInString(d.bottomBarMsg) - 1
280280

281-
buf.WriteString(escape.REVERSE_COLOR)
282-
buf.WriteString(escape.SelectGraphicRendition(d.bottomBarColor))
281+
buf.WriteString(ansi.SGR(ansi.REVERSE_COLOR))
282+
buf.WriteString(ansi.SGR(d.bottomBarColor))
283283
buf.WriteString(fmt.Sprintf("%s %*s\r\n", d.bottomBarMsg, padding, d.bottomRightCorner))
284284

285-
buf.WriteString(escape.ATTRIBUTES_OFF)
286-
buf.WriteString(escape.DEFAULT_FG_COLOR)
285+
buf.WriteString(ansi.SGR(ansi.ALL_ATTRIBUTES_OFF))
286+
buf.WriteString(ansi.SGR(ansi.DEFAULT_FG_COLOR))
287287
}
288288

289289
func (d *display) RefreshScreen() {
@@ -292,9 +292,9 @@ func (d *display) RefreshScreen() {
292292

293293
buf := &bytes.Buffer{}
294294

295-
buf.WriteString(escape.ERASE_ENTIRE_SCREEN)
296-
buf.WriteString(escape.HIDE_CURSOR)
297-
buf.WriteString(escape.MoveCursor(1, 1))
295+
buf.WriteString(ansi.Erase(ansi.ERASE_ENTIRE_SCREEN))
296+
buf.WriteString(ansi.HideCursor())
297+
buf.WriteString(ansi.MoveCursor(1, 1))
298298

299299
switch d.currentSection {
300300

@@ -311,9 +311,9 @@ func (d *display) RefreshScreen() {
311311

312312
d.draw(buf)
313313

314-
buf.WriteString(escape.MoveCursor(d.cy, d.cx))
314+
buf.WriteString(ansi.MoveCursor(d.cy, d.cx))
315315
if d.editingMode {
316-
buf.WriteString(escape.SHOW_CURSOR)
316+
buf.WriteString(ansi.ShowCursor())
317317
}
318318

319319
fmt.Fprint(os.Stdout, buf)

pkg/display/input.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ import (
66
"time"
77
"unicode/utf8"
88

9+
"github.com/giulianopz/newscanoe/pkg/ansi"
910
"github.com/giulianopz/newscanoe/pkg/ascii"
10-
"github.com/giulianopz/newscanoe/pkg/escape"
1111
"github.com/giulianopz/newscanoe/pkg/util"
1212
"golang.org/x/sys/unix"
1313
)
@@ -244,7 +244,7 @@ func (d *display) whileEditing(input byte) {
244244
{
245245
d.setBottomMessage(urlsListSectionMsg)
246246
d.setTmpBottomMessage(1*time.Second, "editing aborted!")
247-
d.exitEditingMode(escape.WHITE)
247+
d.exitEditingMode(ansi.WHITE)
248248
d.resetCoordinates()
249249
}
250250
default:

pkg/display/loading.go

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import (
99
"strings"
1010
"time"
1111

12-
"github.com/giulianopz/newscanoe/pkg/escape"
12+
"github.com/giulianopz/newscanoe/pkg/ansi"
1313
"github.com/giulianopz/newscanoe/pkg/html"
1414
"github.com/giulianopz/newscanoe/pkg/util"
1515
)
@@ -74,15 +74,12 @@ func (d *display) loadFeed(url string) {
7474
return
7575
}
7676

77-
title := strings.TrimSpace(parsedFeed.Title)
78-
7977
if err := d.cache.AddFeed(parsedFeed, url); err != nil {
8078
log.Default().Println(err)
8179
d.setTmpBottomMessage(3*time.Second, fmt.Sprintf("cannot load feed from url: %s", url))
8280
return
8381
}
8482

85-
d.rendered[d.currentRow()] = []byte(title)
8683
d.currentFeedUrl = url
8784

8885
go func() {
@@ -112,16 +109,12 @@ func (d *display) loadAllFeeds() {
112109
return
113110
}
114111

115-
title := strings.TrimSpace(parsedFeed.Title)
116-
117112
if err := d.cache.AddFeed(parsedFeed, url); err != nil {
118113
log.Default().Println(err)
119114
d.setTmpBottomMessage(3*time.Second, fmt.Sprintf("cannot load feed from url: %s", url))
120115
return
121116
}
122117

123-
d.rendered[id] = []byte(title)
124-
125118
d.setBottomMessage(fmt.Sprintf("loading all feeds, please wait........%d/%d", id+1, len(d.raw)))
126119
d.RefreshScreen()
127120
}
@@ -149,6 +142,8 @@ func (d *display) loadArticlesList(url string) {
149142
return
150143
}
151144

145+
cachedFeed.New = false
146+
152147
d.resetRows()
153148

154149
for _, item := range cachedFeed.GetItemsOrderedByDate() {
@@ -173,6 +168,12 @@ func (d *display) loadArticlesList(url string) {
173168
}
174169

175170
d.setBottomMessage(fmt.Sprintf("%s %s %s", articlesListSectionMsg, browserHelp, lynxHelp))
171+
172+
go func() {
173+
if err := d.cache.Encode(); err != nil {
174+
log.Default().Println(err.Error())
175+
}
176+
}()
176177
}
177178
}
178179
}
@@ -196,6 +197,8 @@ func (d *display) loadArticleText(url string) {
196197
return
197198
}
198199

200+
i.New = false
201+
199202
d.resetRows()
200203

201204
scanner := bufio.NewScanner(strings.NewReader(text))
@@ -214,6 +217,13 @@ func (d *display) loadArticleText(url string) {
214217
d.currentSection = ARTICLE_TEXT
215218

216219
d.setBottomMessage(articleTextSectionMsg)
220+
221+
go func() {
222+
if err := d.cache.Encode(); err != nil {
223+
log.Default().Println(err.Error())
224+
}
225+
}()
226+
217227
break
218228
}
219229
}
@@ -226,15 +236,15 @@ func (d *display) addEnteredFeedUrl() {
226236
url := strings.TrimSpace(strings.Join(d.editingBuf, ""))
227237

228238
if !d.canBeParsed(url) {
229-
d.bottomBarColor = escape.RED
239+
d.bottomBarColor = ansi.RED
230240
d.setTmpBottomMessage(3*time.Second, "feed url not valid!")
231241
return
232242
}
233243

234244
if err := util.AppendUrl(url); err != nil {
235245
log.Default().Println(err)
236246

237-
d.bottomBarColor = escape.RED
247+
d.bottomBarColor = ansi.RED
238248

239249
var target *util.UrlAlreadyPresentErr
240250
if errors.As(err, &target) {
@@ -246,7 +256,6 @@ func (d *display) addEnteredFeedUrl() {
246256
}
247257

248258
d.appendToRaw(url)
249-
d.appendToRendered(url)
250259

251260
d.cx = 1
252261
d.cy = len(d.rendered) % (d.height - BOTTOM_PADDING)
@@ -256,5 +265,5 @@ func (d *display) addEnteredFeedUrl() {
256265

257266
d.setBottomMessage(urlsListSectionMsg)
258267
d.setTmpBottomMessage(3*time.Second, "new feed saved!")
259-
d.exitEditingMode(escape.GREEN)
268+
d.exitEditingMode(ansi.GREEN)
260269
}

0 commit comments

Comments
 (0)