oldnews 0.3.0__tar.gz → 0.4.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.
Files changed (33) hide show
  1. {oldnews-0.3.0 → oldnews-0.4.0}/PKG-INFO +1 -1
  2. {oldnews-0.3.0 → oldnews-0.4.0}/pyproject.toml +1 -1
  3. {oldnews-0.3.0 → oldnews-0.4.0}/src/oldnews/commands/__init__.py +10 -2
  4. {oldnews-0.3.0 → oldnews-0.4.0}/src/oldnews/commands/main.py +30 -2
  5. {oldnews-0.3.0 → oldnews-0.4.0}/src/oldnews/providers/main.py +10 -2
  6. {oldnews-0.3.0 → oldnews-0.4.0}/src/oldnews/screens/main.py +88 -10
  7. {oldnews-0.3.0/src/oldnews/sync → oldnews-0.4.0/src/oldnews}/sync.py +3 -3
  8. {oldnews-0.3.0 → oldnews-0.4.0}/src/oldnews/widgets/navigation.py +14 -0
  9. oldnews-0.3.0/src/oldnews/sync/__init__.py +0 -11
  10. {oldnews-0.3.0 → oldnews-0.4.0}/README.md +0 -0
  11. {oldnews-0.3.0 → oldnews-0.4.0}/src/oldnews/__init__.py +0 -0
  12. {oldnews-0.3.0 → oldnews-0.4.0}/src/oldnews/__main__.py +0 -0
  13. {oldnews-0.3.0 → oldnews-0.4.0}/src/oldnews/data/__init__.py +0 -0
  14. {oldnews-0.3.0 → oldnews-0.4.0}/src/oldnews/data/auth.py +0 -0
  15. {oldnews-0.3.0 → oldnews-0.4.0}/src/oldnews/data/config.py +0 -0
  16. {oldnews-0.3.0 → oldnews-0.4.0}/src/oldnews/data/db.py +0 -0
  17. {oldnews-0.3.0 → oldnews-0.4.0}/src/oldnews/data/last_grab.py +0 -0
  18. {oldnews-0.3.0 → oldnews-0.4.0}/src/oldnews/data/local_articles.py +0 -0
  19. {oldnews-0.3.0 → oldnews-0.4.0}/src/oldnews/data/local_folders.py +0 -0
  20. {oldnews-0.3.0 → oldnews-0.4.0}/src/oldnews/data/local_subscriptions.py +0 -0
  21. {oldnews-0.3.0 → oldnews-0.4.0}/src/oldnews/data/local_unread.py +0 -0
  22. {oldnews-0.3.0 → oldnews-0.4.0}/src/oldnews/data/locations.py +0 -0
  23. {oldnews-0.3.0 → oldnews-0.4.0}/src/oldnews/data/navigation_state.py +0 -0
  24. {oldnews-0.3.0 → oldnews-0.4.0}/src/oldnews/data/reset.py +0 -0
  25. {oldnews-0.3.0 → oldnews-0.4.0}/src/oldnews/oldnews.py +0 -0
  26. {oldnews-0.3.0 → oldnews-0.4.0}/src/oldnews/providers/__init__.py +0 -0
  27. {oldnews-0.3.0 → oldnews-0.4.0}/src/oldnews/py.typed +0 -0
  28. {oldnews-0.3.0 → oldnews-0.4.0}/src/oldnews/screens/__init__.py +0 -0
  29. {oldnews-0.3.0 → oldnews-0.4.0}/src/oldnews/screens/login.py +0 -0
  30. {oldnews-0.3.0 → oldnews-0.4.0}/src/oldnews/widgets/__init__.py +0 -0
  31. {oldnews-0.3.0 → oldnews-0.4.0}/src/oldnews/widgets/_after_highlight.py +0 -0
  32. {oldnews-0.3.0 → oldnews-0.4.0}/src/oldnews/widgets/article_content.py +0 -0
  33. {oldnews-0.3.0 → oldnews-0.4.0}/src/oldnews/widgets/article_list.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: oldnews
3
- Version: 0.3.0
3
+ Version: 0.4.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
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "oldnews"
3
- version = "0.3.0"
3
+ version = "0.4.0"
4
4
  description = "A terminal-based client for TheOldReader"
5
5
  readme = "README.md"
6
6
  authors = [
@@ -3,12 +3,16 @@
3
3
  ##############################################################################
4
4
  # Local imports.
5
5
  from .main import (
6
+ Copy,
7
+ CopyArticleToClipboard,
8
+ CopyFeedToClipboard,
9
+ CopyHomePageToClipboard,
6
10
  Escape,
7
11
  MarkAllRead,
8
12
  Next,
9
13
  NextUnread,
10
14
  OpenArticle,
11
- OpenOrigin,
15
+ OpenHomePage,
12
16
  Previous,
13
17
  PreviousUnread,
14
18
  RefreshFromTheOldReader,
@@ -18,12 +22,16 @@ from .main import (
18
22
  ##############################################################################
19
23
  # Exports.
20
24
  __all__ = [
25
+ "Copy",
26
+ "CopyArticleToClipboard",
27
+ "CopyFeedToClipboard",
28
+ "CopyHomePageToClipboard",
21
29
  "Escape",
22
30
  "MarkAllRead",
23
31
  "Next",
24
32
  "NextUnread",
25
33
  "OpenArticle",
26
- "OpenOrigin",
34
+ "OpenHomePage",
27
35
  "Previous",
28
36
  "PreviousUnread",
29
37
  "RefreshFromTheOldReader",
@@ -64,8 +64,8 @@ class OpenArticle(Command):
64
64
 
65
65
 
66
66
  ##############################################################################
67
- class OpenOrigin(Command):
68
- """Open the origin for the current article in the web browser"""
67
+ class OpenHomePage(Command):
68
+ """Open the home page for the current subscription in the web browser"""
69
69
 
70
70
  BINDING_KEY = "O"
71
71
 
@@ -77,4 +77,32 @@ class MarkAllRead(Command):
77
77
  BINDING_KEY = "R"
78
78
 
79
79
 
80
+ ##############################################################################
81
+ class CopyHomePageToClipboard(Command):
82
+ """Copy the URL of the current subscription's home page to the clipboard"""
83
+
84
+ BINDING_KEY = "f3"
85
+
86
+
87
+ ##############################################################################
88
+ class CopyFeedToClipboard(Command):
89
+ """Copy the URL of the current subscription's feed to the clipboard"""
90
+
91
+ BINDING_KEY = "f4"
92
+
93
+
94
+ ##############################################################################
95
+ class CopyArticleToClipboard(Command):
96
+ """Copy the URL for the current article to the clipboard"""
97
+
98
+ BINDING_KEY = "f5"
99
+
100
+
101
+ ##############################################################################
102
+ class Copy(Command):
103
+ """Copy a URL to the clipboard depending on the context"""
104
+
105
+ BINDING_KEY = "ctrl+c"
106
+
107
+
80
108
  ### main.py ends here
@@ -13,12 +13,16 @@ from textual_enhanced.commands import (
13
13
  ##############################################################################
14
14
  # Local imports.
15
15
  from ..commands import (
16
+ Copy,
17
+ CopyArticleToClipboard,
18
+ CopyFeedToClipboard,
19
+ CopyHomePageToClipboard,
16
20
  Escape,
17
21
  MarkAllRead,
18
22
  Next,
19
23
  NextUnread,
20
24
  OpenArticle,
21
- OpenOrigin,
25
+ OpenHomePage,
22
26
  Previous,
23
27
  PreviousUnread,
24
28
  RefreshFromTheOldReader,
@@ -42,8 +46,12 @@ class MainCommands(CommandsProvider):
42
46
  yield from self.maybe(Previous)
43
47
  yield from self.maybe(PreviousUnread)
44
48
  yield from self.maybe(OpenArticle)
45
- yield from self.maybe(OpenOrigin)
49
+ yield from self.maybe(OpenHomePage)
46
50
  yield from self.maybe(MarkAllRead)
51
+ yield from self.maybe(CopyHomePageToClipboard)
52
+ yield from self.maybe(CopyFeedToClipboard)
53
+ yield from self.maybe(CopyArticleToClipboard)
54
+ yield from self.maybe(Copy)
47
55
  yield ToggleShowAll()
48
56
  yield RefreshFromTheOldReader()
49
57
  yield ChangeTheme()
@@ -38,12 +38,16 @@ from textual_enhanced.screen import EnhancedScreen
38
38
  # Local imports.
39
39
  from .. import __version__
40
40
  from ..commands import (
41
+ Copy,
42
+ CopyArticleToClipboard,
43
+ CopyFeedToClipboard,
44
+ CopyHomePageToClipboard,
41
45
  Escape,
42
46
  MarkAllRead,
43
47
  Next,
44
48
  NextUnread,
45
49
  OpenArticle,
46
- OpenOrigin,
50
+ OpenHomePage,
47
51
  Previous,
48
52
  PreviousUnread,
49
53
  RefreshFromTheOldReader,
@@ -117,6 +121,13 @@ class Main(EnhancedScreen[None]):
117
121
  width: 25%;
118
122
  }
119
123
 
124
+ #article-view {
125
+ display: none;
126
+ &.--has-articles {
127
+ display: block;
128
+ }
129
+ }
130
+
120
131
  ArticleList {
121
132
  height: 1fr;
122
133
  }
@@ -142,8 +153,12 @@ class Main(EnhancedScreen[None]):
142
153
  Previous,
143
154
  PreviousUnread,
144
155
  OpenArticle,
145
- OpenOrigin,
156
+ OpenHomePage,
146
157
  ChangeTheme,
158
+ CopyHomePageToClipboard,
159
+ CopyFeedToClipboard,
160
+ CopyArticleToClipboard,
161
+ Copy,
147
162
  ]
148
163
 
149
164
  BINDINGS = Command.bindings(*COMMAND_MESSAGES)
@@ -208,7 +223,7 @@ class Main(EnhancedScreen[None]):
208
223
  yield Navigation(classes="panel").data_bind(
209
224
  Main.folders, Main.subscriptions, Main.unread
210
225
  )
211
- with Vertical():
226
+ with Vertical(id="article-view"):
212
227
  yield ArticleList(classes="panel").data_bind(
213
228
  Main.articles, Main.current_category
214
229
  )
@@ -237,8 +252,14 @@ class Main(EnhancedScreen[None]):
237
252
  # but okay let's be defensive... (when I can come up with a nice
238
253
  # little MRE I'll report it).
239
254
  return True
240
- if action in (OpenArticle.action_name(), OpenOrigin.action_name()):
255
+ if action in (OpenArticle.action_name(), CopyArticleToClipboard.action_name()):
241
256
  return self.article is not None
257
+ if action in (
258
+ OpenHomePage.action_name(),
259
+ CopyFeedToClipboard.action_name(),
260
+ CopyHomePageToClipboard.action_name(),
261
+ ):
262
+ return self.query_one(Navigation).current_subscription is not None
242
263
  if action in (Next.action_name(), Previous.action_name()):
243
264
  return self.articles is not None
244
265
  if action in (
@@ -255,6 +276,11 @@ class Main(EnhancedScreen[None]):
255
276
  return self.articles is not None and any(
256
277
  article.is_unread for article in self.articles
257
278
  )
279
+ if action == Copy.action_name():
280
+ return (
281
+ (navigation := self.query_one(Navigation)).has_focus
282
+ and navigation.current_subscription is not None
283
+ ) or self.query_one("#article-view").has_focus_within
258
284
  return True
259
285
 
260
286
  @on(SubTitle)
@@ -300,6 +326,13 @@ class Main(EnhancedScreen[None]):
300
326
  """Refresh the content of the article list."""
301
327
  if self.current_category:
302
328
  self.articles = get_local_articles(self.current_category, not self.show_all)
329
+ # If the result is there's nothing showing, tidy up the content
330
+ # side of the display and maybe move focus back to navigation.
331
+ if not self.articles:
332
+ self.article = None
333
+ if self.query_one("#article-view").has_focus_within:
334
+ self.query_one(Navigation).focus()
335
+ self.query_one("#article-view").set_class(bool(self.articles), "--has-articles")
303
336
 
304
337
  @work(thread=True, exclusive=True)
305
338
  def _load_locally(self) -> None:
@@ -494,17 +527,62 @@ class Main(EnhancedScreen[None]):
494
527
  severity="error",
495
528
  )
496
529
 
497
- def action_open_origin_command(self) -> None:
498
- """Open the origin of the current article in the web browser."""
499
- if self.article is not None:
500
- if self.article.origin.html_url:
501
- open_url(self.article.origin.html_url)
530
+ def action_open_home_page_command(self) -> None:
531
+ """Open the home page of the current subscription in the web browser."""
532
+ if subscription := self.query_one(Navigation).current_subscription:
533
+ if subscription.html_url:
534
+ open_url(subscription.html_url)
502
535
  else:
503
536
  self.notify(
504
- "No URL available for the article's origin",
537
+ "No home page URL available for the subscription",
505
538
  severity="error",
506
539
  title="Can't visit",
507
540
  )
508
541
 
542
+ def _copy_to_clipboard(self, content: str | None, empty_error: str) -> None:
543
+ """Copy some content to the clipboard.
544
+
545
+ Args:
546
+ content: The content to copy to the clipboard.
547
+ empty_error: The message to show if there's no content.
548
+ """
549
+ if content:
550
+ self.app.copy_to_clipboard(content)
551
+ self.notify("Copied to clipboard")
552
+ else:
553
+ self.notify(empty_error, severity="error", title="Can't copy")
554
+
555
+ def action_copy_home_page_to_clipboard_command(self) -> None:
556
+ """Copy the URL of the current subscription's homepage to the clipboard."""
557
+ if subscription := self.query_one(Navigation).current_subscription:
558
+ self._copy_to_clipboard(
559
+ subscription.html_url, "No home page URL available for the subscription"
560
+ )
561
+
562
+ def action_copy_feed_to_clipboard_command(self) -> None:
563
+ """Copy the URL of the current subscription's feed to the clipboard."""
564
+ if subscription := self.query_one(Navigation).current_subscription:
565
+ self._copy_to_clipboard(
566
+ subscription.url, "No feed URL available for the subscription"
567
+ )
568
+
569
+ def action_copy_article_to_clipboard_command(self) -> None:
570
+ """Copy the URL of the current article to the clipboard."""
571
+ if self.article:
572
+ self._copy_to_clipboard(
573
+ self.article.html_url, "No URL available for the article"
574
+ )
575
+
576
+ def action_copy_command(self) -> None:
577
+ """Copy a URL to the clipboard depending on the current context."""
578
+ if (navigation := self.query_one(Navigation)).has_focus:
579
+ if navigation.current_subscription:
580
+ self.action_copy_home_page_to_clipboard_command()
581
+ elif self.query_one("#article-view").has_focus_within:
582
+ if self.article:
583
+ self.action_copy_article_to_clipboard_command()
584
+ else:
585
+ self.action_copy_home_page_to_clipboard_command()
586
+
509
587
 
510
588
  ### main.py ends here
@@ -11,7 +11,7 @@ from typing import Any, Callable
11
11
  from oldas import ArticleIDs, Articles, Folders, Session, Subscriptions
12
12
 
13
13
  ##############################################################################
14
- from ..data import (
14
+ from .data import (
15
15
  LocalUnread,
16
16
  get_local_unread,
17
17
  get_unread_article_ids,
@@ -71,10 +71,10 @@ class ToRSync:
71
71
 
72
72
  async def _download_newest_articles(self) -> None:
73
73
  """Download the latest articles available."""
74
+ new_grab = datetime.now(timezone.utc)
74
75
  last_grabbed = last_grabbed_data_at() or (
75
- datetime.now() - timedelta(days=load_configuration().local_history)
76
+ new_grab - timedelta(days=load_configuration().local_history)
76
77
  )
77
- new_grab = datetime.now(timezone.utc)
78
78
  loaded = 0
79
79
  async for article in Articles.stream_new_since(
80
80
  self.session, last_grabbed, n=10
@@ -238,6 +238,20 @@ class Navigation(EnhancedOptionList):
238
238
  return selected.subscription
239
239
  raise ValueError("Unknown category")
240
240
 
241
+ @property
242
+ def current_folder(self) -> Folder | None:
243
+ """The current folder, if one is highlighted, or `None`"""
244
+ if isinstance(current := self.current_category, Folder):
245
+ return current
246
+ return None
247
+
248
+ @property
249
+ def current_subscription(self) -> Subscription | None:
250
+ """The current subscription, if one is highlighted, or `None`."""
251
+ if isinstance(current := self.current_category, Subscription):
252
+ return current
253
+ return None
254
+
241
255
  def _highlight_unread(self, direction: HighlightDirection) -> bool:
242
256
  """Highlight the next category with unread articles, if there is one.
243
257
 
@@ -1,11 +0,0 @@
1
- """Code to sync the local data from TheOldReader."""
2
-
3
- ##############################################################################
4
- # Local imports.
5
- from .sync import ToRSync
6
-
7
- ##############################################################################
8
- # Exports.
9
- __all__ = ["ToRSync"]
10
-
11
- ### __init__.py ends here
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes