Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 18 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,24 @@ pipx install lugus

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

### Search

#### Generic Search

![generic search](https://raw.githubusercontent.com/OpenCode/lugus/main/readme/search.png)

#### Title Search

![title search](https://raw.githubusercontent.com/OpenCode/lugus/main/readme/search_title.png)

#### Content Search

![content search](https://raw.githubusercontent.com/OpenCode/lugus/main/readme/search_content.png)

#### Multiple Search

![multi search](https://raw.githubusercontent.com/OpenCode/lugus/main/readme/search_multi.png)

### New Feed Screen

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

## License
Expand Down
Binary file modified readme/home.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added readme/search.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added readme/search_content.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added readme/search_multi.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added readme/search_title.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion src/lugus/__about__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: 2024-present Francesco Apruzzese <[email protected]>
#
# SPDX-License-Identifier: MIT
__version__ = "0.2.2"
__version__ = "0.2.3"
107 changes: 89 additions & 18 deletions src/lugus/lugus.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from textual.widgets import Header, Footer, ListItem, ListView, Rule, Select, Tree, Markdown, Static, Button, \
Label, Input, Pretty, Switch
from textual.reactive import reactive
from rich.emoji import Emoji


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

def update_where(self, table, where, values):
cursor = self.connection.cursor()
cursor.execute(
"UPDATE %s SET %s WHERE %s" % (
table,
", ".join([
f"{v[0]} = {self._convert_value(v[1])}"
for v in values
]),
where,
)
)
self.connection.commit()

def read(self, table, id_record):
if isinstance(id_record, str):
id_record = int(id_record.lower().replace("id-", ""))
Expand Down Expand Up @@ -245,15 +260,20 @@ def compose(self) -> ComposeResult:
},
)
yield tree
yield Button(
"Sync",
variant="primary",
id="sync",
)
yield Button(
"Add New Feed",
variant="primary",
id="add_new",
yield Horizontal(
Button(
Emoji.replace(":counterclockwise_arrows_button:"),
variant="primary",
id="sync",
tooltip="Get new articles from feeds",
),
Button(
Emoji.replace(":heavy_plus_sign:"),
variant="primary",
id="add_new",
tooltip="Add a new feed",
),
id="feed_buttons",
)


Expand All @@ -263,18 +283,41 @@ class Articles(Vertical):
recomposes = reactive(0, recompose=True)
feed = reactive(False, recompose=True)
filter_articles_type = reactive("", recompose=True)
filter_articles = reactive("", recompose=True)

def compose(self) -> ComposeResult:
articles = []
if self.feed and self.feed.data:
data = self.feed.data
# Filter by Read/Unread
if self.filter_articles_type == "unread":
read = " AND read = 0"
elif self.filter_articles_type == "read":
read = " AND read = 1"
else:
read = ""
articles = self.orm.search("article", ["id", "date", "title"], where=f"feed_id = {data["id"]}{read}")
# Filter by Title/Content
if self.filter_articles:
filters = self.filter_articles.split(",")
filter_text = ""
for filter in filters:
filter = filter.strip().replace("\"", "")
if filter:
if filter.lower().startswith("title:"):
filter = filter.replace("title:", "")
filter_text = f"{filter_text} AND title LIKE \"%{filter}%\""
elif filter.lower().startswith("content:"):
filter = filter.replace("content:", "")
filter_text = f"{filter_text} AND content LIKE \"%{filter}%\""
else:
filter_text = f"{filter_text} AND (title LIKE \"%{filter}%\" OR content LIKE \"%{filter}%\")"
else:
filter_text = ""
articles = self.orm.search(
"article",
["id", "date", "title"],
where=f"feed_id = {data["id"]}{read}{filter_text}",
)
yield ListView(
*[
ListItem(
Expand All @@ -292,15 +335,24 @@ def compose(self) -> ComposeResult:

class ArticlesArea(Vertical):

orm = ORM()

def compose(self) -> ComposeResult:
yield Select(
(
("Unread", "unread"),
("Read", "read"),
("All", "all"),
yield Horizontal(
Select(
(
("Unread", "unread"),
("Read", "read"),
("All", "all"),
),
value="unread",
id="filter_articles_type",
),
Input(
placeholder="Search " + Emoji.replace(":magnifying_glass_tilted_left:"),
id="search_articles",
),
value="unread",
id="filter_articles_type",
classes="w100 hauto mb1",
)
yield Articles(
id="articles"
Expand All @@ -311,6 +363,11 @@ def select_changed(self, event: Select.Changed) -> None:
if event.select.id == "filter_articles_type":
self.query_one("#articles").filter_articles_type = event.value

@on(Input.Changed)
def filter_articles(self, event: Input.Changed) -> None:
if event.input.id == "search_articles":
self.query_one("#articles").filter_articles = event.value


class Reader(Vertical):

Expand Down Expand Up @@ -351,6 +408,7 @@ class LugusApp(App):
("c", "open_configuration", "Configs"),
("question_mark", "article_data", "Article Raw Data"),
("r", "article_read", "Set Article as Read"),
("a", "all_articles_read", "Set all Feed Articles as Read"),
("o", "read_online", "Read Online"),
]

Expand Down Expand Up @@ -423,7 +481,7 @@ def _synchronize_feeds(self, update_interface_element=None):
)
if update_interface_element:
update_interface_element.disabled = False
update_interface_element.label = "Sync"
update_interface_element.label = Emoji.replace(":counterclockwise_arrows_button:")
self.notify("Feeds synchronized!")
return

Expand Down Expand Up @@ -486,6 +544,19 @@ def action_article_read(self) -> None:
def action_open_configuration(self) -> None:
self.status = "configuration"

def action_all_articles_read(self) -> None:
feed = self.query_one("#articles").feed
if not feed:
self.notify("Select a feed", severity="warning")
else:
self.orm.update_where(
"article",
f"feed_id = {feed.data["id"]} AND read = 0",
[("read", 1)],
)
self.query_one("#sidebar").recomposes += 1
self.query_one("#articles").recomposes += 1

def action_read_online(self) -> None:
reader = self.query_one("#reader")
if not reader.id_article:
Expand Down
37 changes: 33 additions & 4 deletions src/lugus/style.tcss
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,26 @@ Screen {
layout: horizontal;
}

.wauto {
width: auto;
}

.w100 {
width: 100%;
}

.hauto {
height: auto;
}

.h100 {
height: 100%;
}

.mb1 {
margin-bottom: 1;
}

.full_form {
width: 100%;
height: 100%;
Expand Down Expand Up @@ -45,17 +57,24 @@ Screen {

#feedsbar {
width: 100%;
height: 90%;
height: 95%;
}

#sync {
#feed_buttons {
width: 100%;
height: 5%;
}

#sync {
width: auto;
height: auto;
margin-right: 1;
}

#add_new {
width: 100%;
height: 5%;
width: auto;
height: auto;
margin-right: 1;
}

#articles_area {
Expand Down Expand Up @@ -122,3 +141,13 @@ Screen {
width: 100%;
}

#filter_articles_type {
width: 30%;
height: auto;
}

#search_articles {
width: 70%;
height: auto;
}