oldnews 0.8.0__py3-none-any.whl → 0.9.0__py3-none-any.whl

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/screens/main.py CHANGED
@@ -3,7 +3,7 @@
3
3
  ##############################################################################
4
4
  # Python imports.
5
5
  from dataclasses import dataclass
6
- from datetime import datetime, timedelta
6
+ from datetime import UTC, datetime, timedelta
7
7
  from webbrowser import open as open_url
8
8
 
9
9
  ##############################################################################
@@ -363,10 +363,12 @@ class Main(EnhancedScreen[None]):
363
363
  self.unread = message.counts
364
364
  self.post_message(self.SubTitle(""))
365
365
 
366
- def _refresh_article_list(self) -> None:
366
+ async def _refresh_article_list(self) -> None:
367
367
  """Refresh the content of the article list."""
368
368
  if self.current_category:
369
- self.articles = get_local_articles(self.current_category, not self.show_all)
369
+ self.articles = await get_local_articles(
370
+ self.current_category, not self.show_all
371
+ )
370
372
  # If the result is there's nothing showing, tidy up the content
371
373
  # side of the display and maybe move focus back to navigation.
372
374
  if not self.articles:
@@ -375,32 +377,32 @@ class Main(EnhancedScreen[None]):
375
377
  self.navigation.focus()
376
378
  self.article_view.set_class(bool(self.articles), "--has-articles")
377
379
 
378
- @work(thread=True, exclusive=True)
379
- def _load_locally(self) -> None:
380
+ @work(exclusive=True)
381
+ async def _load_locally(self) -> None:
380
382
  """Load up any locally-held data."""
381
- if subscriptions := get_local_subscriptions():
383
+ if subscriptions := await get_local_subscriptions():
382
384
  self.post_message(self.NewSubscriptions(subscriptions))
383
- if folders := get_local_folders():
385
+ if folders := await get_local_folders():
384
386
  self.post_message(self.NewFolders(folders))
385
- if cleaned := clean_old_read_articles(
387
+ if cleaned := await clean_old_read_articles(
386
388
  timedelta(days=load_configuration().local_history)
387
389
  ):
388
390
  self.notify(f"Old read articles cleaned from local storage: {cleaned}")
389
- if unread := get_local_unread(folders, subscriptions):
391
+ if unread := await get_local_unread(folders, subscriptions):
390
392
  self.post_message(self.NewUnread(unread))
391
- self._refresh_article_list()
393
+ await self._refresh_article_list()
392
394
  # If we've never grabbed data from ToR before, or if it's been long enough...
393
- if (last_grabbed := last_grabbed_data_at()) is None or (
394
- (datetime.now() - last_grabbed).seconds
395
+ if (last_grabbed := await last_grabbed_data_at()) is None or (
396
+ (datetime.now(UTC) - last_grabbed).seconds
395
397
  >= load_configuration().startup_refresh_holdoff_period
396
398
  ):
397
399
  # ...kick off a refresh from TheOldReader.
398
400
  self.post_message(RefreshFromTheOldReader())
399
401
 
400
402
  @on(SyncFinished)
401
- def _sync_finished(self) -> None:
403
+ async def _sync_finished(self) -> None:
402
404
  """Clean up after a sync from TheOldReader has finished."""
403
- self._refresh_article_list()
405
+ await self._refresh_article_list()
404
406
  self.post_message(self.SubTitle(""))
405
407
 
406
408
  @on(RefreshFromTheOldReader)
@@ -422,7 +424,9 @@ class Main(EnhancedScreen[None]):
422
424
  ).sync()
423
425
 
424
426
  @on(Navigation.CategorySelected)
425
- def _handle_navigaion_selection(self, message: Navigation.CategorySelected) -> None:
427
+ async def _handle_navigaion_selection(
428
+ self, message: Navigation.CategorySelected
429
+ ) -> None:
426
430
  """Handle a navigation selection being made.
427
431
 
428
432
  Args:
@@ -430,12 +434,12 @@ class Main(EnhancedScreen[None]):
430
434
  """
431
435
  self.current_category = message.category
432
436
  self.article = None
433
- self._refresh_article_list()
437
+ await self._refresh_article_list()
434
438
  self.article_list.focus()
435
439
 
436
- def _watch_show_all(self) -> None:
440
+ async def _watch_show_all(self) -> None:
437
441
  """Handle changes to the show all flag."""
438
- self._refresh_article_list()
442
+ await self._refresh_article_list()
439
443
 
440
444
  @work
441
445
  async def _mark_read(self, article: Article) -> None:
@@ -444,11 +448,11 @@ class Main(EnhancedScreen[None]):
444
448
  Args:
445
449
  article: The article to mark as read.
446
450
  """
447
- locally_mark_read(article)
451
+ await locally_mark_read(article)
448
452
  self.post_message(
449
- self.NewUnread(get_local_unread(self.folders, self.subscriptions))
453
+ self.NewUnread(await get_local_unread(self.folders, self.subscriptions))
450
454
  )
451
- self._refresh_article_list()
455
+ await self._refresh_article_list()
452
456
  await article.mark_read(self._session)
453
457
 
454
458
  @on(ArticleList.ViewArticle)
@@ -544,7 +548,10 @@ class Main(EnhancedScreen[None]):
544
548
  ]
545
549
  ):
546
550
  return
547
- category_description = f"{current_category.__class__.__name__.lower()} '{current_category.name if isinstance(current_category, Folder) else current_category.title}'"
551
+ category_description = (
552
+ f"{current_category.__class__.__name__.lower()} "
553
+ f"'{current_category.name if isinstance(current_category, Folder) else current_category.title}'"
554
+ )
548
555
  plural = "s" if len(ids_to_mark_read) > 1 else ""
549
556
  if await self.app.push_screen_wait(
550
557
  Confirm(
@@ -554,11 +561,13 @@ class Main(EnhancedScreen[None]):
554
561
  )
555
562
  ):
556
563
  if await self._session.add_tag(ids_to_mark_read, State.READ):
557
- locally_mark_article_ids_read(ids_to_mark_read)
564
+ await locally_mark_article_ids_read(ids_to_mark_read)
558
565
  self.post_message(
559
- self.NewUnread(get_local_unread(self.folders, self.subscriptions))
566
+ self.NewUnread(
567
+ await get_local_unread(self.folders, self.subscriptions)
568
+ )
560
569
  )
561
- self._refresh_article_list()
570
+ await self._refresh_article_list()
562
571
  self.notify(
563
572
  f"{len(ids_to_mark_read)} article{plural} marked read for {category_description}"
564
573
  )
@@ -687,7 +696,7 @@ class Main(EnhancedScreen[None]):
687
696
  ModalInput("Subscription name", folder.name)
688
697
  ):
689
698
  if await Folders.rename(self._session, folder, new_name):
690
- rename_folder_for_articles(folder, new_name)
699
+ await rename_folder_for_articles(folder, new_name)
691
700
  self.notify("Renamed")
692
701
  self.post_message(RefreshFromTheOldReader())
693
702
  else:
@@ -711,11 +720,11 @@ class Main(EnhancedScreen[None]):
711
720
  if await self.app.push_screen_wait(
712
721
  Confirm(
713
722
  f"Remove {subscription.title}?",
714
- f"Are you sure you wish to remove the subscription?",
723
+ "Are you sure you wish to remove the subscription?",
715
724
  )
716
725
  ):
717
726
  if await Subscriptions.remove(self._session, subscription):
718
- remove_subscription_articles(subscription)
727
+ await remove_subscription_articles(subscription)
719
728
  self.notify(f"Removed {subscription.title}")
720
729
  self.post_message(RefreshFromTheOldReader())
721
730
  else:
@@ -734,7 +743,7 @@ class Main(EnhancedScreen[None]):
734
743
  )
735
744
  ):
736
745
  if await Folders.remove(self._session, folder):
737
- remove_folder_from_articles(folder)
746
+ await remove_folder_from_articles(folder)
738
747
  self.notify(f"Removed {folder.name}")
739
748
  self.post_message(RefreshFromTheOldReader())
740
749
  else:
@@ -751,25 +760,24 @@ class Main(EnhancedScreen[None]):
751
760
  @work
752
761
  async def action_move_subscription_command(self) -> None:
753
762
  """Move a subscription to a different folder."""
754
- if subscription := self.navigation.current_subscription:
755
- if (
756
- target_folder := await self.app.push_screen_wait(
757
- FolderInput(self.folders)
763
+ if not (subscription := self.navigation.current_subscription):
764
+ return
765
+ if (
766
+ target_folder := await self.app.push_screen_wait(FolderInput(self.folders))
767
+ ) is not None:
768
+ if await Subscriptions.move(self._session, subscription, target_folder):
769
+ await move_subscription_articles(
770
+ subscription, subscription.folder_id, target_folder
771
+ )
772
+ self.notify("Moved")
773
+ self.post_message(RefreshFromTheOldReader())
774
+ else:
775
+ self.notify(
776
+ f"Could not move the subscription into '{target_folder}'",
777
+ title="Move failed",
778
+ timeout=8,
779
+ markup=False,
758
780
  )
759
- ) is not None:
760
- if await Subscriptions.move(self._session, subscription, target_folder):
761
- move_subscription_articles(
762
- subscription, subscription.folder_id, target_folder
763
- )
764
- self.notify("Moved")
765
- self.post_message(RefreshFromTheOldReader())
766
- else:
767
- self.notify(
768
- f"Could not move the subscription into '{target_folder}'",
769
- title="Move failed",
770
- timeout=8,
771
- markup=False,
772
- )
773
781
 
774
782
  @work
775
783
  async def action_information_command(self) -> None:
oldnews/sync.py CHANGED
@@ -2,9 +2,10 @@
2
2
 
3
3
  ##############################################################################
4
4
  # Python imports.
5
+ from collections.abc import AsyncIterator, Callable, Iterable
5
6
  from dataclasses import dataclass
6
- from datetime import datetime, timedelta, timezone
7
- from typing import Any, AsyncIterator, Callable, Final, Iterable
7
+ from datetime import UTC, datetime, timedelta
8
+ from typing import Any
8
9
 
9
10
  ##############################################################################
10
11
  # OldAS imports.
@@ -41,10 +42,6 @@ type Callback = Callable[[], Any] | None
41
42
  type CallbackWith[T] = Callable[[T], Any] | None
42
43
  """Type of callback with a single argument."""
43
44
 
44
- ##############################################################################
45
- BATCH_SIZE: Final[int] = 10
46
- """Batch size for downloading articles."""
47
-
48
45
 
49
46
  ##############################################################################
50
47
  @dataclass
@@ -68,9 +65,11 @@ class TheOldReaderSync:
68
65
 
69
66
  def __post_init__(self) -> None:
70
67
  """Initialise the sync object."""
71
- self._last_sync = last_grabbed_data_at()
68
+ self._batch_size = load_configuration().article_download_batch_size
69
+ """The size of the article batch to download and save."""
70
+ self._last_sync: datetime | None = None
72
71
  """The time at which we last did a sync."""
73
- self._first_sync = self._last_sync is None
72
+ self._first_sync = True
74
73
  """Is this our first ever sync?"""
75
74
 
76
75
  def _step(self, step: str, *, log: bool = True) -> None:
@@ -106,21 +105,25 @@ class TheOldReaderSync:
106
105
  The number of articles downloaded.
107
106
  """
108
107
  loaded = 0
108
+ save_batch: list[Article] = []
109
109
  async for article in stream:
110
110
  # I've encountered articles that don't have an origin stream ID,
111
111
  # which means that I can't relate them back to a stream, which
112
112
  # means I'll never see them anyway...
113
113
  if not article.origin.stream_id:
114
114
  continue
115
- # TODO: Right now I'm saving articles one at a time; perhaps I
116
- # should save them in small batches? This would be simple enough
117
- # -- perhaps same them in batches the same size as the buffer
118
- # window I'm using right now (currently BATCH_SIZE articles per
119
- # trip to ToR).
120
- save_local_articles(Articles([article]))
115
+ save_batch.append(article)
121
116
  loaded += 1
122
- if (loaded % BATCH_SIZE) == 0:
117
+ if (loaded % self._batch_size) == 0:
123
118
  self._step(f"{description}: {loaded}", log=False)
119
+ Log().debug(f"Saving batch of articles: {len(save_batch)}")
120
+ await save_local_articles(Articles(save_batch))
121
+ Log().debug(f"Saved batch of articles: {len(save_batch)}")
122
+ save_batch = []
123
+ if save_batch:
124
+ Log().debug(f"Saving final batch of articles: {len(save_batch)}")
125
+ await save_local_articles(Articles(save_batch))
126
+ Log().debug(f"Saved final batch of articles: {len(save_batch)}")
124
127
  return loaded
125
128
 
126
129
  async def _get_updated_read_status(self) -> None:
@@ -128,14 +131,14 @@ class TheOldReaderSync:
128
131
  if self._first_sync:
129
132
  return
130
133
  self._step("Syncing read/unread status with TheOldReader")
131
- remote_unread_articles = set(
134
+ remote_unread_articles = {
132
135
  article_id.full_id
133
136
  for article_id in await ArticleIDs.load_unread(self.session)
134
- )
135
- local_unread_articles = set(get_unread_article_ids())
137
+ }
138
+ local_unread_articles = set(await get_unread_article_ids())
136
139
  if mark_as_read := local_unread_articles - remote_unread_articles:
137
140
  Log().debug(f"Articles found as marked read elsewhere: {mark_as_read}")
138
- locally_mark_article_ids_read(mark_as_read)
141
+ await locally_mark_article_ids_read(mark_as_read)
139
142
  self._result(
140
143
  f"Articles found read elsewhere on TheOldReader: {len(mark_as_read)}"
141
144
  )
@@ -146,13 +149,11 @@ class TheOldReaderSync:
146
149
  Args:
147
150
  subscriptions: The subscriptions to download the backlog for.
148
151
  """
149
- cutoff = datetime.now(timezone.utc) - timedelta(
150
- days=load_configuration().local_history
151
- )
152
+ cutoff = datetime.now(UTC) - timedelta(days=load_configuration().local_history)
152
153
  for subscription in subscriptions:
153
154
  if loaded := await self._download(
154
155
  Articles.stream_new_since(
155
- self.session, cutoff, subscription, n=BATCH_SIZE
156
+ self.session, cutoff, subscription, n=self._batch_size
156
157
  ),
157
158
  f"Downloading article backlog for {subscription.title}",
158
159
  ):
@@ -167,7 +168,7 @@ class TheOldReaderSync:
167
168
  The folders.
168
169
  """
169
170
  self._step("Getting folder list")
170
- folders = save_local_folders(await Folders.load(self.session))
171
+ folders = await save_local_folders(await Folders.load(self.session))
171
172
  if self.on_new_folders:
172
173
  self.on_new_folders(folders)
173
174
  return folders
@@ -180,8 +181,10 @@ class TheOldReaderSync:
180
181
  after.
181
182
  """
182
183
  self._step("Getting subscriptions list")
183
- original_subscriptions = get_local_subscriptions()
184
- subscriptions = save_local_subscriptions(await Subscriptions.load(self.session))
184
+ original_subscriptions = await get_local_subscriptions()
185
+ subscriptions = await save_local_subscriptions(
186
+ await Subscriptions.load(self.session)
187
+ )
185
188
  if self.on_new_subscriptions:
186
189
  self.on_new_subscriptions(subscriptions)
187
190
  return original_subscriptions, subscriptions
@@ -193,18 +196,18 @@ class TheOldReaderSync:
193
196
  if self._first_sync
194
197
  else f"Getting new articles since {self._last_sync}"
195
198
  )
196
- new_grab = datetime.now(timezone.utc)
199
+ new_grab = datetime.now(UTC)
197
200
  last_grabbed = self._last_sync or (
198
201
  new_grab - timedelta(days=load_configuration().local_history)
199
202
  )
200
203
  if loaded := await self._download(
201
- Articles.stream_new_since(self.session, last_grabbed, n=BATCH_SIZE),
204
+ Articles.stream_new_since(self.session, last_grabbed, n=self._batch_size),
202
205
  "Downloading articles from TheOldReader",
203
206
  ):
204
207
  self._result(f"Articles downloaded: {loaded}")
205
208
  else:
206
209
  self._result("No new articles found on TheOldReader")
207
- remember_we_last_grabbed_at(new_grab)
210
+ await remember_we_last_grabbed_at(new_grab)
208
211
 
209
212
  @staticmethod
210
213
  def _set_of_ids(subscriptions: Subscriptions) -> set[str]:
@@ -247,7 +250,7 @@ class TheOldReaderSync:
247
250
  if subscription.id in new_subscriptions
248
251
  )
249
252
 
250
- def _clean_orphaned_articles(
253
+ async def _clean_orphaned_articles(
251
254
  self,
252
255
  original_subscriptions: Subscriptions,
253
256
  current_subscriptions: Subscriptions,
@@ -265,9 +268,9 @@ class TheOldReaderSync:
265
268
  ):
266
269
  Log().info(f"Found remotely-removed subscriptions: {removed_subscriptions}")
267
270
  for subscription in removed_subscriptions:
268
- remove_subscription_articles(subscription)
271
+ await remove_subscription_articles(subscription)
269
272
 
270
- def _get_unread_counts(
273
+ async def _get_unread_counts(
271
274
  self, folders: Folders, subscriptions: Subscriptions
272
275
  ) -> None:
273
276
  """Get the updated unread counts.
@@ -277,17 +280,19 @@ class TheOldReaderSync:
277
280
  subscriptions: The subscriptions to get the counts for.
278
281
  """
279
282
  if self.on_new_unread:
280
- self.on_new_unread(get_local_unread(folders, subscriptions))
283
+ self.on_new_unread(await get_local_unread(folders, subscriptions))
281
284
 
282
285
  async def sync(self) -> None:
283
286
  """Sync the data from TheOldReader."""
287
+ self._last_sync = await last_grabbed_data_at()
288
+ self._first_sync = self._last_sync is None
284
289
  folders = await self._get_folders()
285
290
  original_subscriptions, subscriptions = await self._get_subscriptions()
286
291
  await self._get_new_articles()
287
292
  await self._get_updated_read_status()
288
293
  await self._get_historical_articles(original_subscriptions, subscriptions)
289
- self._clean_orphaned_articles(original_subscriptions, subscriptions)
290
- self._get_unread_counts(folders, subscriptions)
294
+ await self._clean_orphaned_articles(original_subscriptions, subscriptions)
295
+ await self._get_unread_counts(folders, subscriptions)
291
296
  if self.on_sync_finished:
292
297
  self.on_sync_finished()
293
298
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  ##############################################################################
4
4
  # Python imports.
5
- from typing import Callable
5
+ from collections.abc import Callable
6
6
 
7
7
  ##############################################################################
8
8
  # BagOfStuff imports.
@@ -153,15 +153,16 @@ class ArticleList(EnhancedOptionList):
153
153
  `True` if an unread article was found and highlighted, `False`
154
154
  if not.
155
155
  """
156
- if next_hit := next_matching_option(
157
- cast(list[ArticleView], self.options),
158
- self.highlighted,
159
- direction,
160
- attrgetter("article.is_unread"),
161
- ):
162
- if next_hit.id is not None:
163
- self.highlighted = self.get_option_index(next_hit.id)
164
- return True
156
+ if (
157
+ next_hit := next_matching_option(
158
+ cast(list[ArticleView], self.options),
159
+ self.highlighted,
160
+ direction,
161
+ attrgetter("article.is_unread"),
162
+ )
163
+ ) and next_hit.id is not None:
164
+ self.highlighted = self.get_option_index(next_hit.id)
165
+ return True
165
166
  self.notify("No more unread articles")
166
167
  return False
167
168
 
@@ -6,8 +6,9 @@ from __future__ import annotations
6
6
 
7
7
  ##############################################################################
8
8
  # Python imports.
9
+ from collections.abc import Callable, Iterable, Iterator
9
10
  from dataclasses import dataclass
10
- from typing import Callable, Iterable, Iterator, cast
11
+ from typing import cast
11
12
 
12
13
  ##############################################################################
13
14
  # OldAs imports.
@@ -154,9 +155,13 @@ class Navigation(EnhancedOptionList):
154
155
  classes: The CSS classes of the navigation widget.
155
156
  """
156
157
  super().__init__(id=id, classes=classes)
157
- self._expanded = get_navigation_state()
158
+ self._expanded: set[str] = set()
158
159
  """The IDs of the folders that are expanded."""
159
160
 
161
+ def on_mount(self) -> None:
162
+ """Configure the widget once the DOM is mounted."""
163
+ self.call_next(self._load_state)
164
+
160
165
  @staticmethod
161
166
  def _key(attr: str) -> Callable[[object], str]:
162
167
  """Create a key to use with `sorted`.
@@ -250,14 +255,18 @@ class Navigation(EnhancedOptionList):
250
255
  """React to the unread data being updated."""
251
256
  self._refresh_navigation()
252
257
 
253
- @work(thread=True)
254
- def _save_state(self, state: set[str]) -> None:
258
+ async def _load_state(self) -> None:
259
+ """Load the navigation state."""
260
+ self._expanded = await get_navigation_state()
261
+
262
+ @work
263
+ async def _save_state(self, state: set[str]) -> None:
255
264
  """Save the folder expanded/collapsed state.
256
265
 
257
266
  Args:
258
267
  state: The state to save.
259
268
  """
260
- save_navigation_state(state)
269
+ await save_navigation_state(state)
261
270
 
262
271
  def _set_expansion(self, new_state: set[str]) -> None:
263
272
  """Set the new navigation state.
@@ -349,15 +358,16 @@ class Navigation(EnhancedOptionList):
349
358
  `True` if an unread category was found and highlighted, `False`
350
359
  if not.
351
360
  """
352
- if next_hit := next_matching_option(
353
- cast(list[FolderView | SubscriptionView], self.options),
354
- self.highlighted,
355
- direction,
356
- self._contains_unread,
357
- ):
358
- if next_hit.id is not None:
359
- self.highlighted = self.get_option_index(next_hit.id)
360
- return True
361
+ if (
362
+ next_hit := next_matching_option(
363
+ cast(list[FolderView | SubscriptionView], self.options),
364
+ self.highlighted,
365
+ direction,
366
+ self._contains_unread,
367
+ )
368
+ ) and next_hit.id is not None:
369
+ self.highlighted = self.get_option_index(next_hit.id)
370
+ return True
361
371
  self.notify("No more folders or subscriptions with unread articles")
362
372
  return False
363
373
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: oldnews
3
- Version: 0.8.0
3
+ Version: 0.9.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
@@ -20,7 +20,7 @@ Requires-Dist: oldas>=0.10.0
20
20
  Requires-Dist: textual>=6.3.0
21
21
  Requires-Dist: textual-autocomplete>=4.0.6
22
22
  Requires-Dist: textual-enhanced>=1.2.0
23
- Requires-Dist: typedal>=4.2.2
23
+ Requires-Dist: tortoise-orm>=0.25.4
24
24
  Requires-Dist: xdg-base-dirs>=6.0.2
25
25
  Requires-Python: >=3.12
26
26
  Project-URL: Homepage, https://github.com/davep/oldnews
@@ -0,0 +1,44 @@
1
+ oldnews/__init__.py,sha256=p5a3l8kly6VYQ4IjiC6s03Nj-FaLDTOzqU1ZC6WZLl0,540
2
+ oldnews/__main__.py,sha256=ly786aBy3mi6DxDAimvHJ3QFjj-y8-7EKD_0F8YXf4Q,4648
3
+ oldnews/commands/__init__.py,sha256=R8w9ccEYqZ4AKdtg2vvT_ejgdC3rsQ7yiTVnYVWDLOQ,1042
4
+ oldnews/commands/main.py,sha256=lPfOIYwf8L7eYG9GUy2MDvoj4fl3ZikjY8Ld-dmv7i0,4089
5
+ oldnews/data/__init__.py,sha256=vzka2owD1vv63iDFU2SXXirzOPELHcfLVRWmCnegnTI,2158
6
+ oldnews/data/auth.py,sha256=5CWu4kjEkyJ8TDqqIYkUNFAzebygpksKIPdFhT62uOM,1217
7
+ oldnews/data/config.py,sha256=bxy-rN-GKqdg2Lxh4exRL3pvzqiqt-YFZdISFoM8poE,3441
8
+ oldnews/data/dump.py,sha256=sgWz1HHJlxG_B8uDtwtt_TS1ulnP4yji3hK7iinMc7Q,2206
9
+ oldnews/data/last_grab.py,sha256=hiXU4AEpi0U1ED4T8HaClZOZfcv1rIytDDsd3Ll3WzY,1384
10
+ oldnews/data/local_articles.py,sha256=kEGKxP7p7Xu3fbYoKLWrXAwv_OU3ausym4yUuheUJ_U,10894
11
+ oldnews/data/local_data.py,sha256=vhfJ8-GYHf_n-kQV4fII-4tHKSRVBakFNQP0fww2ByQ,1359
12
+ oldnews/data/local_folders.py,sha256=JTojYhRdx4q8d_ngoaeWVMsJXEZKhbHqKqxFDmWBdww,1387
13
+ oldnews/data/local_subscriptions.py,sha256=UngKYLzK6umsScSwumc2P4634KqhOawkYJD8guOOMlM,2714
14
+ oldnews/data/local_unread.py,sha256=TcjEeU4QRZRh1gjgDSL239nolgOOlLgWkO8zeRsBPn0,1466
15
+ oldnews/data/locations.py,sha256=rJqXy_15ok5G86XbrXmixyYTBLFO0V0BWD8rLKotuBg,1723
16
+ oldnews/data/log.py,sha256=nd6w7NAiUzZjeVM4HZd-oWFFVyQvWblXjdRYlSjua5k,1211
17
+ oldnews/data/models/__init__.py,sha256=nRwT1Nrh30x8Hed_327Qvslkus8faUQTlQ8ToYTN57Q,718
18
+ oldnews/data/models/local_article.py,sha256=0tiR1lpeR54zqMGqzvVsNhfghGUaIU1jH9rX39lquPk,3043
19
+ oldnews/data/models/local_folder.py,sha256=ZOq1kKBne_Hjewddkm2T7VidJ2vGR41F5QzoJ-rfotA,586
20
+ oldnews/data/models/local_state.py,sha256=-4JWUdxBSuT5RhNOwI0HwaFOlj_QWMyJoIPCLGtG7wU,798
21
+ oldnews/data/models/local_subscription.py,sha256=TkNa20lL79j_JCq5zT2vYXqjG9EmMZP_GN0km8XgGsE,1533
22
+ oldnews/data/navigation_state.py,sha256=nEVC1BxT-BofbSesDraCfmsFufeX2lBbqEct17zY4P8,1106
23
+ oldnews/data/reset.py,sha256=VNItdDmiDnnueroM1errUdL77yMKJY2_ZZkfZHkXeoI,825
24
+ oldnews/oldnews.py,sha256=8LNb8fEH5VDmt0GJqHtWdHSRlYvZ6v8ddMgWrpCpd2A,4088
25
+ oldnews/providers/__init__.py,sha256=R_wS987EuI3KDiN9Xzf0P5wiRSYLOk_-3twvr2ML7WU,354
26
+ oldnews/providers/main.py,sha256=kHyXfumWV6pZhLUfAe6LCsEYQmITYmV0RxXIn0qmlNs,2020
27
+ oldnews/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
28
+ oldnews/screens/__init__.py,sha256=-Iif16T316dnZkgJjX1YE7e4v7FxI0k_V08qd5za0t8,330
29
+ oldnews/screens/folder_input.py,sha256=i6KeRVD1Ucfg2T6VJmzdKKnUHf6qo_1ka0ZLlNMuabA,1869
30
+ oldnews/screens/information_display.py,sha256=D2H7k9NjvyytLGk4FXs169VMV-ajG3UwFrUzdfQhvKM,2552
31
+ oldnews/screens/login.py,sha256=a2hUERxpKYg7GQIYlpyDgHTAUEKmsANjwHZx1r-gXSw,3377
32
+ oldnews/screens/main.py,sha256=S3nrIE9ZgGw1qOD-3tWhQ-CYSRCwT8nQn1ZP9GR7arw,28963
33
+ oldnews/screens/new_subscription.py,sha256=bk01RXWu3kvJbtm88o1mrYpll73Op3PWWl7ggIH29kk,4011
34
+ oldnews/screens/process_subscription.py,sha256=8Z7aJLx-KkItaCQXfgf44cOQ83AZPlRJcMxFDuaHk54,2891
35
+ oldnews/sync.py,sha256=3IbfqWwzMfVK6Uy-651hn2_PCUunthH25-nhPXhJiLk,11491
36
+ oldnews/widgets/__init__.py,sha256=5VSjKswHxv2W8g0O-LWlUFS-gy7iQrIM8PGvSBhDNLU,438
37
+ oldnews/widgets/_next_matching_option.py,sha256=ZXBXiQCZHc-m6yz4xH63onvDUkxnPalgjZdk55DPEAo,1609
38
+ oldnews/widgets/article_content.py,sha256=pv8zobd3eTsALHYmP854dd_enqy32QXGoWXEILYnWfA,3553
39
+ oldnews/widgets/article_list.py,sha256=bbp8J-9Hw3_xRObkqLjIg7_sJp14pK6soG2qMtFXrSQ,7396
40
+ oldnews/widgets/navigation.py,sha256=ItK8genhD2zYTkaOONSF4wR9tScu8oVDQziTzXu4qAI,13012
41
+ oldnews-0.9.0.dist-info/WHEEL,sha256=5DEXXimM34_d4Gx1AuF9ysMr1_maoEtGKjaILM3s4w4,80
42
+ oldnews-0.9.0.dist-info/entry_points.txt,sha256=FxY6Y4IsHZubhtdd0QJG3p2kCTcXe0e-Ib_AW0qkotE,51
43
+ oldnews-0.9.0.dist-info/METADATA,sha256=_m8xdUNW56bLykPeS7iM7-FbhgZeMAWhcBFvdh21Kd0,2957
44
+ oldnews-0.9.0.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: uv 0.9.28
2
+ Generator: uv 0.9.29
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any