oldnews 0.0.2__tar.gz → 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {oldnews-0.0.2 → oldnews-0.1.0}/PKG-INFO +4 -4
- {oldnews-0.0.2 → oldnews-0.1.0}/pyproject.toml +1 -1
- {oldnews-0.0.2 → oldnews-0.1.0}/src/oldnews/commands/__init__.py +6 -0
- {oldnews-0.0.2 → oldnews-0.1.0}/src/oldnews/commands/main.py +21 -0
- {oldnews-0.0.2 → oldnews-0.1.0}/src/oldnews/providers/main.py +6 -0
- {oldnews-0.0.2 → oldnews-0.1.0}/src/oldnews/screens/main.py +71 -10
- {oldnews-0.0.2 → oldnews-0.1.0}/src/oldnews/widgets/article_list.py +22 -4
- {oldnews-0.0.2 → oldnews-0.1.0}/README.md +0 -0
- {oldnews-0.0.2 → oldnews-0.1.0}/src/oldnews/__init__.py +0 -0
- {oldnews-0.0.2 → oldnews-0.1.0}/src/oldnews/__main__.py +0 -0
- {oldnews-0.0.2 → oldnews-0.1.0}/src/oldnews/data/__init__.py +0 -0
- {oldnews-0.0.2 → oldnews-0.1.0}/src/oldnews/data/auth.py +0 -0
- {oldnews-0.0.2 → oldnews-0.1.0}/src/oldnews/data/config.py +0 -0
- {oldnews-0.0.2 → oldnews-0.1.0}/src/oldnews/data/db.py +0 -0
- {oldnews-0.0.2 → oldnews-0.1.0}/src/oldnews/data/last_grab.py +0 -0
- {oldnews-0.0.2 → oldnews-0.1.0}/src/oldnews/data/local_articles.py +0 -0
- {oldnews-0.0.2 → oldnews-0.1.0}/src/oldnews/data/local_folders.py +0 -0
- {oldnews-0.0.2 → oldnews-0.1.0}/src/oldnews/data/local_subscriptions.py +0 -0
- {oldnews-0.0.2 → oldnews-0.1.0}/src/oldnews/data/local_unread.py +0 -0
- {oldnews-0.0.2 → oldnews-0.1.0}/src/oldnews/data/locations.py +0 -0
- {oldnews-0.0.2 → oldnews-0.1.0}/src/oldnews/data/navigation_state.py +0 -0
- {oldnews-0.0.2 → oldnews-0.1.0}/src/oldnews/oldnews.py +0 -0
- {oldnews-0.0.2 → oldnews-0.1.0}/src/oldnews/providers/__init__.py +0 -0
- {oldnews-0.0.2 → oldnews-0.1.0}/src/oldnews/py.typed +0 -0
- {oldnews-0.0.2 → oldnews-0.1.0}/src/oldnews/screens/__init__.py +0 -0
- {oldnews-0.0.2 → oldnews-0.1.0}/src/oldnews/screens/login.py +0 -0
- {oldnews-0.0.2 → oldnews-0.1.0}/src/oldnews/widgets/__init__.py +0 -0
- {oldnews-0.0.2 → oldnews-0.1.0}/src/oldnews/widgets/article_content.py +0 -0
- {oldnews-0.0.2 → oldnews-0.1.0}/src/oldnews/widgets/navigation.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: oldnews
|
|
3
|
-
Version: 0.0
|
|
3
|
+
Version: 0.1.0
|
|
4
4
|
Summary: A terminal-based client for TheOldReader
|
|
5
5
|
Keywords: atom,client,Google Reader,RSS,TheOldReader,terminal,news-reader,news
|
|
6
6
|
Author: Dave Pearson
|
|
@@ -22,12 +22,12 @@ Requires-Dist: textual-enhanced>=1.2.0
|
|
|
22
22
|
Requires-Dist: typedal>=4.2.2
|
|
23
23
|
Requires-Dist: xdg-base-dirs>=6.0.2
|
|
24
24
|
Requires-Python: >=3.11
|
|
25
|
-
Project-URL: Discussions, https://github.com/davep/oldnews/discussions
|
|
26
|
-
Project-URL: Documentation, https://oldnews.davep.dev/
|
|
27
25
|
Project-URL: Homepage, https://github.com/davep/oldnews
|
|
28
|
-
Project-URL: Issues, https://github.com/davep/oldnews/issues
|
|
29
26
|
Project-URL: Repository, https://github.com/davep/oldnews
|
|
27
|
+
Project-URL: Documentation, https://oldnews.davep.dev/
|
|
30
28
|
Project-URL: Source, https://github.com/davep/oldnews
|
|
29
|
+
Project-URL: Issues, https://github.com/davep/oldnews/issues
|
|
30
|
+
Project-URL: Discussions, https://github.com/davep/oldnews/discussions
|
|
31
31
|
Description-Content-Type: text/markdown
|
|
32
32
|
|
|
33
33
|
# OldNews - A TheOldReader client for the terminal
|
|
@@ -4,8 +4,11 @@
|
|
|
4
4
|
# Local imports.
|
|
5
5
|
from .main import (
|
|
6
6
|
Escape,
|
|
7
|
+
MarkAllRead,
|
|
8
|
+
Next,
|
|
7
9
|
NextUnread,
|
|
8
10
|
OpenArticle,
|
|
11
|
+
Previous,
|
|
9
12
|
PreviousUnread,
|
|
10
13
|
RefreshFromTheOldReader,
|
|
11
14
|
ToggleShowAll,
|
|
@@ -15,8 +18,11 @@ from .main import (
|
|
|
15
18
|
# Exports.
|
|
16
19
|
__all__ = [
|
|
17
20
|
"Escape",
|
|
21
|
+
"MarkAllRead",
|
|
22
|
+
"Next",
|
|
18
23
|
"NextUnread",
|
|
19
24
|
"OpenArticle",
|
|
25
|
+
"Previous",
|
|
20
26
|
"PreviousUnread",
|
|
21
27
|
"RefreshFromTheOldReader",
|
|
22
28
|
"ToggleShowAll",
|
|
@@ -35,6 +35,13 @@ class NextUnread(Command):
|
|
|
35
35
|
BINDING_KEY = "n"
|
|
36
36
|
|
|
37
37
|
|
|
38
|
+
##############################################################################
|
|
39
|
+
class Next(Command):
|
|
40
|
+
"""Navigate to the next article regardless of read status"""
|
|
41
|
+
|
|
42
|
+
BINDING_KEY = "N"
|
|
43
|
+
|
|
44
|
+
|
|
38
45
|
##############################################################################
|
|
39
46
|
class PreviousUnread(Command):
|
|
40
47
|
"""Navigate to the previous unread article in the currently-selected category"""
|
|
@@ -42,6 +49,13 @@ class PreviousUnread(Command):
|
|
|
42
49
|
BINDING_KEY = "p"
|
|
43
50
|
|
|
44
51
|
|
|
52
|
+
##############################################################################
|
|
53
|
+
class Previous(Command):
|
|
54
|
+
"""Navigate to the next article regardless of read status"""
|
|
55
|
+
|
|
56
|
+
BINDING_KEY = "P"
|
|
57
|
+
|
|
58
|
+
|
|
45
59
|
##############################################################################
|
|
46
60
|
class OpenArticle(Command):
|
|
47
61
|
"""Open the current article in the web browser"""
|
|
@@ -49,4 +63,11 @@ class OpenArticle(Command):
|
|
|
49
63
|
BINDING_KEY = "o"
|
|
50
64
|
|
|
51
65
|
|
|
66
|
+
##############################################################################
|
|
67
|
+
class MarkAllRead(Command):
|
|
68
|
+
"""Mark all unread articles in the current category as read"""
|
|
69
|
+
|
|
70
|
+
BINDING_KEY = "R"
|
|
71
|
+
|
|
72
|
+
|
|
52
73
|
### main.py ends here
|
|
@@ -14,8 +14,11 @@ from textual_enhanced.commands import (
|
|
|
14
14
|
# Local imports.
|
|
15
15
|
from ..commands import (
|
|
16
16
|
Escape,
|
|
17
|
+
MarkAllRead,
|
|
18
|
+
Next,
|
|
17
19
|
NextUnread,
|
|
18
20
|
OpenArticle,
|
|
21
|
+
Previous,
|
|
19
22
|
PreviousUnread,
|
|
20
23
|
RefreshFromTheOldReader,
|
|
21
24
|
ToggleShowAll,
|
|
@@ -33,9 +36,12 @@ class MainCommands(CommandsProvider):
|
|
|
33
36
|
The commands for the command palette.
|
|
34
37
|
"""
|
|
35
38
|
yield Escape()
|
|
39
|
+
yield from self.maybe(Next)
|
|
36
40
|
yield from self.maybe(NextUnread)
|
|
41
|
+
yield from self.maybe(Previous)
|
|
37
42
|
yield from self.maybe(PreviousUnread)
|
|
38
43
|
yield from self.maybe(OpenArticle)
|
|
44
|
+
yield from self.maybe(MarkAllRead)
|
|
39
45
|
yield ToggleShowAll()
|
|
40
46
|
yield RefreshFromTheOldReader()
|
|
41
47
|
yield ChangeTheme()
|
|
@@ -15,6 +15,7 @@ from oldas import (
|
|
|
15
15
|
Folder,
|
|
16
16
|
Folders,
|
|
17
17
|
Session,
|
|
18
|
+
State,
|
|
18
19
|
Subscription,
|
|
19
20
|
Subscriptions,
|
|
20
21
|
)
|
|
@@ -31,6 +32,7 @@ from textual.widgets import Footer, Header
|
|
|
31
32
|
##############################################################################
|
|
32
33
|
# Textual enhanced imports.
|
|
33
34
|
from textual_enhanced.commands import ChangeTheme, Command, Help, Quit
|
|
35
|
+
from textual_enhanced.dialogs import Confirm
|
|
34
36
|
from textual_enhanced.screen import EnhancedScreen
|
|
35
37
|
|
|
36
38
|
##############################################################################
|
|
@@ -38,8 +40,11 @@ from textual_enhanced.screen import EnhancedScreen
|
|
|
38
40
|
from .. import __version__
|
|
39
41
|
from ..commands import (
|
|
40
42
|
Escape,
|
|
43
|
+
MarkAllRead,
|
|
44
|
+
Next,
|
|
41
45
|
NextUnread,
|
|
42
46
|
OpenArticle,
|
|
47
|
+
Previous,
|
|
43
48
|
PreviousUnread,
|
|
44
49
|
RefreshFromTheOldReader,
|
|
45
50
|
ToggleShowAll,
|
|
@@ -134,7 +139,10 @@ class Main(EnhancedScreen[None]):
|
|
|
134
139
|
RefreshFromTheOldReader,
|
|
135
140
|
# Everything else.
|
|
136
141
|
Escape,
|
|
142
|
+
MarkAllRead,
|
|
143
|
+
Next,
|
|
137
144
|
NextUnread,
|
|
145
|
+
Previous,
|
|
138
146
|
PreviousUnread,
|
|
139
147
|
OpenArticle,
|
|
140
148
|
ChangeTheme,
|
|
@@ -230,7 +238,13 @@ class Main(EnhancedScreen[None]):
|
|
|
230
238
|
return True
|
|
231
239
|
if action == OpenArticle.action_name():
|
|
232
240
|
return self.article is not None
|
|
233
|
-
if action in (
|
|
241
|
+
if action in (Next.action_name(), Previous.action_name()):
|
|
242
|
+
return self.articles is not None
|
|
243
|
+
if action in (
|
|
244
|
+
NextUnread.action_name(),
|
|
245
|
+
PreviousUnread.action_name(),
|
|
246
|
+
MarkAllRead.action_name(),
|
|
247
|
+
):
|
|
234
248
|
return self.articles is not None and any(
|
|
235
249
|
article.is_unread for article in self.articles
|
|
236
250
|
)
|
|
@@ -336,12 +350,10 @@ class Main(EnhancedScreen[None]):
|
|
|
336
350
|
self.post_message(self.SubTitle("Comparing against locally-read articles"))
|
|
337
351
|
local_unread_articles = set(get_unread_article_ids())
|
|
338
352
|
if mark_as_read := local_unread_articles - remote_unread_articles:
|
|
339
|
-
self.post_message(
|
|
340
|
-
self.SubTitle(
|
|
341
|
-
f"Articles found read elsewhere on TheOldReader: {len(mark_as_read)}"
|
|
342
|
-
)
|
|
343
|
-
)
|
|
344
353
|
locally_mark_article_ids_read(mark_as_read)
|
|
354
|
+
self.notify(
|
|
355
|
+
f"Articles found read elsewhere on TheOldReader: {len(mark_as_read)}"
|
|
356
|
+
)
|
|
345
357
|
|
|
346
358
|
@on(RefreshFromTheOldReader)
|
|
347
359
|
@work(exclusive=True)
|
|
@@ -459,19 +471,33 @@ class Main(EnhancedScreen[None]):
|
|
|
459
471
|
elif self.focused is self.query_one(Navigation):
|
|
460
472
|
self.app.exit()
|
|
461
473
|
|
|
474
|
+
def action_next_command(self) -> None:
|
|
475
|
+
"""Go to the next article in the currently-viewed category."""
|
|
476
|
+
if self.article is None:
|
|
477
|
+
self.query_one(ArticleList).highlight_next_article()
|
|
478
|
+
else:
|
|
479
|
+
self.query_one(ArticleList).select_next_article()
|
|
480
|
+
|
|
481
|
+
def action_previous_command(self) -> None:
|
|
482
|
+
"""Go to the previous article in the currently-viewed category."""
|
|
483
|
+
if self.article is None:
|
|
484
|
+
self.query_one(ArticleList).highlight_previous_article()
|
|
485
|
+
else:
|
|
486
|
+
self.query_one(ArticleList).select_previous_article()
|
|
487
|
+
|
|
462
488
|
def action_next_unread_command(self) -> None:
|
|
463
489
|
"""Go to the next unread article in the currently-viewed category."""
|
|
464
490
|
if self.article is None:
|
|
465
|
-
self.query_one(ArticleList).
|
|
491
|
+
self.query_one(ArticleList).highlight_next_unread_article()
|
|
466
492
|
else:
|
|
467
|
-
self.query_one(ArticleList).
|
|
493
|
+
self.query_one(ArticleList).select_next_unread_article()
|
|
468
494
|
|
|
469
495
|
def action_previous_unread_command(self) -> None:
|
|
470
496
|
"""Go to the previous unread article in the currently-viewed category"""
|
|
471
497
|
if self.article is None:
|
|
472
|
-
self.query_one(ArticleList).
|
|
498
|
+
self.query_one(ArticleList).highlight_previous_unread_article()
|
|
473
499
|
else:
|
|
474
|
-
self.query_one(ArticleList).
|
|
500
|
+
self.query_one(ArticleList).select_previous_unread_article()
|
|
475
501
|
|
|
476
502
|
def action_open_article_command(self) -> None:
|
|
477
503
|
"""Open the current article in a web browser."""
|
|
@@ -485,5 +511,40 @@ class Main(EnhancedScreen[None]):
|
|
|
485
511
|
title="Can't visit",
|
|
486
512
|
)
|
|
487
513
|
|
|
514
|
+
@work
|
|
515
|
+
async def action_mark_all_read_command(self) -> None:
|
|
516
|
+
"""Mark all unread articles in the current category as read."""
|
|
517
|
+
if (current_category := self.query_one(Navigation).current_category) is None:
|
|
518
|
+
return
|
|
519
|
+
if not (
|
|
520
|
+
ids_to_mark_read := [
|
|
521
|
+
article.id for article in self.articles if article.is_unread
|
|
522
|
+
]
|
|
523
|
+
):
|
|
524
|
+
return
|
|
525
|
+
category_description = f"{current_category.__class__.__name__.lower()} '{current_category.name if isinstance(current_category, Folder) else current_category.title}'"
|
|
526
|
+
plural = "s" if len(ids_to_mark_read) > 1 else ""
|
|
527
|
+
if await self.app.push_screen_wait(
|
|
528
|
+
Confirm(
|
|
529
|
+
"Mark all read",
|
|
530
|
+
f"Are you sure you want to mark all unread articles in the {category_description} as read?\n\n"
|
|
531
|
+
f"This will mark {len(ids_to_mark_read)} article{plural} as read.",
|
|
532
|
+
)
|
|
533
|
+
):
|
|
534
|
+
if await self._session.add_tag(ids_to_mark_read, State.READ):
|
|
535
|
+
locally_mark_article_ids_read(ids_to_mark_read)
|
|
536
|
+
self.post_message(
|
|
537
|
+
self.NewUnread(get_local_unread(self.folders, self.subscriptions))
|
|
538
|
+
)
|
|
539
|
+
self._refresh_article_list()
|
|
540
|
+
self.notify(
|
|
541
|
+
f"{len(ids_to_mark_read)} article{plural} marked read for {category_description}"
|
|
542
|
+
)
|
|
543
|
+
else:
|
|
544
|
+
self.notify(
|
|
545
|
+
"Failed to mark as read on TheOldReader",
|
|
546
|
+
severity="error",
|
|
547
|
+
)
|
|
548
|
+
|
|
488
549
|
|
|
489
550
|
### main.py ends here
|
|
@@ -185,20 +185,38 @@ class ArticleList(EnhancedOptionList):
|
|
|
185
185
|
self.notify("No more unread articles")
|
|
186
186
|
return False
|
|
187
187
|
|
|
188
|
-
def
|
|
188
|
+
def highlight_next_article(self) -> None:
|
|
189
|
+
"""Highlight the next article in the list."""
|
|
190
|
+
self.call_later(self.run_action, "cursor_down")
|
|
191
|
+
|
|
192
|
+
def highlight_previous_article(self) -> None:
|
|
193
|
+
"""Highlight the previous article in the list."""
|
|
194
|
+
self.call_later(self.run_action, "cursor_up")
|
|
195
|
+
|
|
196
|
+
def highlight_next_unread_article(self) -> None:
|
|
189
197
|
"""Highlight the next unread article in the list."""
|
|
190
198
|
self._highlight_unread("next")
|
|
191
199
|
|
|
192
|
-
def
|
|
200
|
+
def highlight_previous_unread_article(self) -> None:
|
|
193
201
|
"""Highlight the previous unread article in the list."""
|
|
194
202
|
self._highlight_unread("previous")
|
|
195
203
|
|
|
196
|
-
def
|
|
204
|
+
def select_next_article(self) -> None:
|
|
205
|
+
"""Select the next article in the list."""
|
|
206
|
+
self.call_later(self.run_action, "cursor_down")
|
|
207
|
+
self.call_later(self.run_action, "select")
|
|
208
|
+
|
|
209
|
+
def select_previous_article(self) -> None:
|
|
210
|
+
"""Select the previous article in the list."""
|
|
211
|
+
self.call_later(self.run_action, "cursor_up")
|
|
212
|
+
self.call_later(self.run_action, "select")
|
|
213
|
+
|
|
214
|
+
def select_next_unread_article(self) -> None:
|
|
197
215
|
"""Select the next unread article in the list."""
|
|
198
216
|
if self._highlight_unread("next"):
|
|
199
217
|
self.call_later(self.run_action, "select")
|
|
200
218
|
|
|
201
|
-
def
|
|
219
|
+
def select_previous_unread_article(self) -> None:
|
|
202
220
|
"""Select the next unread article in the list."""
|
|
203
221
|
if self._highlight_unread("previous"):
|
|
204
222
|
self.call_later(self.run_action, "select")
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|