Skip to content

Commit fa7ed27

Browse files
authored
Merge pull request #8 from OpenCode/improve/set_feed_as_read
Improve/set feed as read
2 parents 6e5de9c + 8bd71fe commit fa7ed27

File tree

9 files changed

+141
-25
lines changed

9 files changed

+141
-25
lines changed

README.md

+18-2
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,24 @@ pipx install lugus
2323

2424
![main page screenshot](https://raw.githubusercontent.com/OpenCode/lugus/main/readme/home.png)
2525

26+
### Search
27+
28+
#### Generic Search
29+
30+
![generic search](https://raw.githubusercontent.com/OpenCode/lugus/main/readme/search.png)
31+
32+
#### Title Search
33+
34+
![title search](https://raw.githubusercontent.com/OpenCode/lugus/main/readme/search_title.png)
35+
36+
#### Content Search
37+
38+
![content search](https://raw.githubusercontent.com/OpenCode/lugus/main/readme/search_content.png)
39+
40+
#### Multiple Search
41+
42+
![multi search](https://raw.githubusercontent.com/OpenCode/lugus/main/readme/search_multi.png)
43+
2644
### New Feed Screen
2745

2846
![new feed page screenshot](https://raw.githubusercontent.com/OpenCode/lugus/main/readme/new_feed.png)
@@ -33,13 +51,11 @@ pipx install lugus
3351
- [ ] Import existing OPML files
3452
- [ ] Export feeds as OPML file
3553
- [ ] Set article as unread
36-
- [ ] Set a feed as read
3754
- [ ] Set an article as favorite
3855
- [ ] Tags system for articles
3956
- [ ] Add notes on articles
4057
- [ ] Feeds auto-sync
4158
- [ ] Edit existing feeds
42-
- [ ] Search articles by name/content
4359
- [ ] Support for Windows
4460

4561
## License

readme/home.png

-38.3 KB
Loading

readme/search.png

218 KB
Loading

readme/search_content.png

183 KB
Loading

readme/search_multi.png

236 KB
Loading

readme/search_title.png

181 KB
Loading

src/lugus/__about__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
# SPDX-FileCopyrightText: 2024-present Francesco Apruzzese <[email protected]>
22
#
33
# SPDX-License-Identifier: MIT
4-
__version__ = "0.2.2"
4+
__version__ = "0.2.3"

src/lugus/lugus.py

+89-18
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
from textual.widgets import Header, Footer, ListItem, ListView, Rule, Select, Tree, Markdown, Static, Button, \
1313
Label, Input, Pretty, Switch
1414
from textual.reactive import reactive
15+
from rich.emoji import Emoji
1516

1617

1718
CONFIG_DIR = Path.joinpath(Path.home(), ".config/lugus")
@@ -77,6 +78,20 @@ def update(self, table, id_record, values):
7778
)
7879
self.connection.commit()
7980

81+
def update_where(self, table, where, values):
82+
cursor = self.connection.cursor()
83+
cursor.execute(
84+
"UPDATE %s SET %s WHERE %s" % (
85+
table,
86+
", ".join([
87+
f"{v[0]} = {self._convert_value(v[1])}"
88+
for v in values
89+
]),
90+
where,
91+
)
92+
)
93+
self.connection.commit()
94+
8095
def read(self, table, id_record):
8196
if isinstance(id_record, str):
8297
id_record = int(id_record.lower().replace("id-", ""))
@@ -245,15 +260,20 @@ def compose(self) -> ComposeResult:
245260
},
246261
)
247262
yield tree
248-
yield Button(
249-
"Sync",
250-
variant="primary",
251-
id="sync",
252-
)
253-
yield Button(
254-
"Add New Feed",
255-
variant="primary",
256-
id="add_new",
263+
yield Horizontal(
264+
Button(
265+
Emoji.replace(":counterclockwise_arrows_button:"),
266+
variant="primary",
267+
id="sync",
268+
tooltip="Get new articles from feeds",
269+
),
270+
Button(
271+
Emoji.replace(":heavy_plus_sign:"),
272+
variant="primary",
273+
id="add_new",
274+
tooltip="Add a new feed",
275+
),
276+
id="feed_buttons",
257277
)
258278

259279

@@ -263,18 +283,41 @@ class Articles(Vertical):
263283
recomposes = reactive(0, recompose=True)
264284
feed = reactive(False, recompose=True)
265285
filter_articles_type = reactive("", recompose=True)
286+
filter_articles = reactive("", recompose=True)
266287

267288
def compose(self) -> ComposeResult:
268289
articles = []
269290
if self.feed and self.feed.data:
270291
data = self.feed.data
292+
# Filter by Read/Unread
271293
if self.filter_articles_type == "unread":
272294
read = " AND read = 0"
273295
elif self.filter_articles_type == "read":
274296
read = " AND read = 1"
275297
else:
276298
read = ""
277-
articles = self.orm.search("article", ["id", "date", "title"], where=f"feed_id = {data["id"]}{read}")
299+
# Filter by Title/Content
300+
if self.filter_articles:
301+
filters = self.filter_articles.split(",")
302+
filter_text = ""
303+
for filter in filters:
304+
filter = filter.strip().replace("\"", "")
305+
if filter:
306+
if filter.lower().startswith("title:"):
307+
filter = filter.replace("title:", "")
308+
filter_text = f"{filter_text} AND title LIKE \"%{filter}%\""
309+
elif filter.lower().startswith("content:"):
310+
filter = filter.replace("content:", "")
311+
filter_text = f"{filter_text} AND content LIKE \"%{filter}%\""
312+
else:
313+
filter_text = f"{filter_text} AND (title LIKE \"%{filter}%\" OR content LIKE \"%{filter}%\")"
314+
else:
315+
filter_text = ""
316+
articles = self.orm.search(
317+
"article",
318+
["id", "date", "title"],
319+
where=f"feed_id = {data["id"]}{read}{filter_text}",
320+
)
278321
yield ListView(
279322
*[
280323
ListItem(
@@ -292,15 +335,24 @@ def compose(self) -> ComposeResult:
292335

293336
class ArticlesArea(Vertical):
294337

338+
orm = ORM()
339+
295340
def compose(self) -> ComposeResult:
296-
yield Select(
297-
(
298-
("Unread", "unread"),
299-
("Read", "read"),
300-
("All", "all"),
341+
yield Horizontal(
342+
Select(
343+
(
344+
("Unread", "unread"),
345+
("Read", "read"),
346+
("All", "all"),
347+
),
348+
value="unread",
349+
id="filter_articles_type",
350+
),
351+
Input(
352+
placeholder="Search " + Emoji.replace(":magnifying_glass_tilted_left:"),
353+
id="search_articles",
301354
),
302-
value="unread",
303-
id="filter_articles_type",
355+
classes="w100 hauto mb1",
304356
)
305357
yield Articles(
306358
id="articles"
@@ -311,6 +363,11 @@ def select_changed(self, event: Select.Changed) -> None:
311363
if event.select.id == "filter_articles_type":
312364
self.query_one("#articles").filter_articles_type = event.value
313365

366+
@on(Input.Changed)
367+
def filter_articles(self, event: Input.Changed) -> None:
368+
if event.input.id == "search_articles":
369+
self.query_one("#articles").filter_articles = event.value
370+
314371

315372
class Reader(Vertical):
316373

@@ -351,6 +408,7 @@ class LugusApp(App):
351408
("c", "open_configuration", "Configs"),
352409
("question_mark", "article_data", "Article Raw Data"),
353410
("r", "article_read", "Set Article as Read"),
411+
("a", "all_articles_read", "Set all Feed Articles as Read"),
354412
("o", "read_online", "Read Online"),
355413
]
356414

@@ -423,7 +481,7 @@ def _synchronize_feeds(self, update_interface_element=None):
423481
)
424482
if update_interface_element:
425483
update_interface_element.disabled = False
426-
update_interface_element.label = "Sync"
484+
update_interface_element.label = Emoji.replace(":counterclockwise_arrows_button:")
427485
self.notify("Feeds synchronized!")
428486
return
429487

@@ -486,6 +544,19 @@ def action_article_read(self) -> None:
486544
def action_open_configuration(self) -> None:
487545
self.status = "configuration"
488546

547+
def action_all_articles_read(self) -> None:
548+
feed = self.query_one("#articles").feed
549+
if not feed:
550+
self.notify("Select a feed", severity="warning")
551+
else:
552+
self.orm.update_where(
553+
"article",
554+
f"feed_id = {feed.data["id"]} AND read = 0",
555+
[("read", 1)],
556+
)
557+
self.query_one("#sidebar").recomposes += 1
558+
self.query_one("#articles").recomposes += 1
559+
489560
def action_read_online(self) -> None:
490561
reader = self.query_one("#reader")
491562
if not reader.id_article:

src/lugus/style.tcss

+33-4
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,26 @@ Screen {
22
layout: horizontal;
33
}
44

5+
.wauto {
6+
width: auto;
7+
}
8+
59
.w100 {
610
width: 100%;
711
}
812

13+
.hauto {
14+
height: auto;
15+
}
16+
917
.h100 {
1018
height: 100%;
1119
}
1220

21+
.mb1 {
22+
margin-bottom: 1;
23+
}
24+
1325
.full_form {
1426
width: 100%;
1527
height: 100%;
@@ -45,17 +57,24 @@ Screen {
4557

4658
#feedsbar {
4759
width: 100%;
48-
height: 90%;
60+
height: 95%;
4961
}
5062

51-
#sync {
63+
#feed_buttons {
5264
width: 100%;
5365
height: 5%;
5466
}
5567

68+
#sync {
69+
width: auto;
70+
height: auto;
71+
margin-right: 1;
72+
}
73+
5674
#add_new {
57-
width: 100%;
58-
height: 5%;
75+
width: auto;
76+
height: auto;
77+
margin-right: 1;
5978
}
6079

6180
#articles_area {
@@ -122,3 +141,13 @@ Screen {
122141
width: 100%;
123142
}
124143

144+
#filter_articles_type {
145+
width: 30%;
146+
height: auto;
147+
}
148+
149+
#search_articles {
150+
width: 70%;
151+
height: auto;
152+
}
153+

0 commit comments

Comments
 (0)