You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

253 lines
9.9 KiB

import urwid
import yaml
from concurrent.futures import ThreadPoolExecutor
from API import Fetcher
from Render import Article
import asyncio
class LeftPane(urwid.ListBox):
def __init__(self, categories):
super().__init__(self)
items = [urwid.AttrMap(urwid.Columns([(5, urwid.Text(str(category["count"]))), urwid.Text(category["name"])]),
(category["id"], category["count"]), "reveal focus") for category in categories]
walker = urwid.SimpleListWalker(items)
self.body = walker
self.categoryPosition = 0
self.isCategoryView = True
self.currentCategory = ""
focus_widget, idx = self.get_focus()
if self.isCategoryView:
self.currentCategory = focus_widget.attr_map[None][0]
def getArticlesFromCategory(self, category, number):
tui.fetcher.articlesFromCategory(category[13:], str(number))
articles = tui.fetcher.articles[category[13:]]
return articles
def getArticlesFromFeed(self, feed):
return tui.fetcher.articlesFromFeed(feed, self.currentCategory[13:])
async def setCategoryArticles(self, attrMap):
itemId = attrMap[0]
number = attrMap[1]
await tui.fetcher.articlesFromCategoryAsync(itemId[13:], str(number))
focus_widget, idx = self.get_focus()
currentCategory = focus_widget.attr_map[None][0]
if itemId == currentCategory:
tui.articles = tui.fetcher.articles[currentCategory[13:]]
tui.articleView.fill(tui.articles)
def setFeedArticles(self, attrMap):
itemId = attrMap
tui.articles = tui.fetcher.articlesFromFeed(itemId, self.currentCategory[13:])
if tui.articles is not None:
tui.articleView.fill(tui.articles)
def keypress(self, size, key):
if key in ("j", "down"):
item_size = len(self.body)
focus_widget, idx = self.get_focus()
if idx < item_size - 1:
idx = idx + 1
self.set_focus(idx)
focus_widget, idx = self.get_focus()
if self.isCategoryView:
self.currentCategory = focus_widget.attr_map[None][0]
asyncio.create_task(self.setCategoryArticles(focus_widget.attr_map[None]))
else:
self.setFeedArticles(focus_widget.attr_map[None])
return
elif key in ("k", "up"):
focus_widget, idx = self.get_focus()
if idx > 0:
idx = idx - 1
self.set_focus(idx)
focus_widget, idx = self.get_focus()
if self.isCategoryView:
self.currentCategory = focus_widget.attr_map[None][0]
asyncio.create_task(self.setCategoryArticles(focus_widget.attr_map[None]))
else:
self.setFeedArticles(focus_widget.attr_map[None])
return
elif key in ("l", "right"):
if self.isCategoryView and tui.fetcher.checkCategory(self.currentCategory[13:]):
self.isCategoryView = False
focus_widget, idx = self.get_focus()
self.categoryPosition = idx
categoryId = focus_widget.attr_map[None][0]
feeds = tui.fetcher.feedsFromCategory(categoryId)
feedItems = [urwid.AttrMap(urwid.Columns([(5, urwid.Text(str(tui.fetcher.unreadCounts[feed["id"]][0]))),
urwid.Text(feed["title"])]), feed["id"], "reveal focus") for feed in feeds]
walker = urwid.SimpleListWalker(feedItems)
self.body = walker
tui.leftBox.set_title(categoryId[13:])
focus_widget, idx = self.get_focus()
if self.isCategoryView:
self.currentCategory = focus_widget.attr_map[None][0]
self.setFeedArticles(focus_widget.attr_map[None])
return
elif key in ("h", "left"):
if not self.isCategoryView:
self.isCategoryView = True
items = [urwid.AttrMap(urwid.Columns([(5, urwid.Text(str(category["count"]))), urwid.Text(category["name"])]),
(category["id"], category["count"]), "reveal focus") for category in tui.fetcher.categories]
walker = urwid.SimpleFocusListWalker(items)
self.body = walker
tui.leftBox.set_title("Categories")
self.set_focus(self.categoryPosition)
focus_widget, idx = self.get_focus()
tui.articles = self.setCategoryArticles(focus_widget.attr_map[None])
return
return super().keypress(size, key)
class RightPane(urwid.ListBox):
def __init__(self, items):
super().__init__(self)
walker = urwid.SimpleListWalker([])
self.body = walker
self.isList = True
self.articlePosition = 0
self.article = None
self.chunkNumber = 0
def fill(self, articles):
items = [urwid.AttrMap(urwid.Columns([(2, urwid.Text("N")), urwid.Text(article["title"])]),
article["id"], "reveal focus") for article in articles]
walker = urwid.SimpleListWalker(items)
self.body = walker
def getArticle(self, articleId):
article = [item for item in tui.fetcher.articles[tui.feedView.currentCategory[13:]] if item["id"] == articleId][0]
return article
def setArticleTitle(self):
tui.rightBox.set_title(self.article.title + " (" + str(self.article.currentPageNumber) + "/" +
str(self.article.numberOfPages) + ")")
def keypress(self, size, key):
if key in ("j", "down"):
if not self.isList:
walker = urwid.SimpleListWalker([urwid.Text(self.article.scrollDown())])
self.body = walker
self.setArticleTitle()
item_size = len(self.body)
focus_widget, idx = self.get_focus()
if idx < item_size - 1:
idx = idx + 1
self.set_focus(idx)
return
elif key in ("k", "up"):
if not self.isList:
walker = urwid.SimpleListWalker([urwid.Text(self.article.scrollUp())])
self.body = walker
self.setArticleTitle()
focus_widget, idx = self.get_focus()
if idx > 0:
idx = idx - 1
self.set_focus(idx)
return
elif key in ("l", "right"):
if self.isList is True:
self.isList = False
self.chunkNumber = 0
focus_widget, idx = self.get_focus()
self.articlePosition = idx
articleId = focus_widget.attr_map[None]
self.article = Article(self.getArticle(articleId))
walker = urwid.SimpleListWalker([urwid.Text(self.article.firstPage)])
self.body = walker
self.setArticleTitle()
return
elif key in ("h", "left"):
if self.isList is False:
self.isList = True
self.fill(tui.articles)
tui.rightBox.set_title("Articles")
self.set_focus(self.articlePosition)
return
return super().keypress(size, key)
class TUI(urwid.Frame):
@classmethod
def create(cls):
tui = cls()
palette = [("linebox", "dark blue", "black"), ("text", "dark cyan", "dark cyan"),
('header', 'white', 'black'), ('reveal focus', 'black', 'dark cyan', 'standout')]
loop = urwid.MainLoop(
tui,
palette,
event_loop=urwid.AsyncioEventLoop(),
unhandled_input=tui.unhandled_input,
)
tui.loop = loop
return tui
def __init__(self):
with open('config.yml', 'r') as file:
config = yaml.safe_load(file)
URL = config["server"]["URL"]
token = config["server"]["token"]
self.fetcher = Fetcher(URL, token)
self.leftPaneItems = {}
self.activePane = False
self.executor = ThreadPoolExecutor(max_workers=1)
self.loop = None
self.feedView = LeftPane(self.fetcher.categories)
self.articleView = RightPane([])
self.leftBox = urwid.LineBox(self.feedView, title="Categories")
self.rightBox = urwid.LineBox(self.articleView, title="Articles")
self.views = urwid.Columns(
[(40, self.leftBox), self.rightBox])
self.status = urwid.Text("")
self.footer = urwid.ListBox([self.status])
self.container = urwid.Pile([self.views, (1, self.footer)])
self.body = self.container
self.articles = []
super().__init__(self.body)
def fetchAll(self):
articles = tui.fetcher.fetch()
for text in articles:
self.status.set_text(text)
tui.loop.entering_idle()
self.status.set_text("")
self.loop.event_loop.remove_alarm(self.handle)
def run(self):
focus_widget, idx = self.feedView.get_focus()
item = focus_widget.attr_map[None][0][13:]
self.fetcher.articlesFromCategory(item, str(focus_widget.attr_map[None][1]))
self.articles = tui.fetcher.articles[item]
self.articleView.fill(self.articles)
self.loop.run()
self.executor.shutdown(wait=False)
def unhandled_input(self, key):
if key == "tab":
self.activePane = not self.activePane
self.views.set_focus(int(self.activePane))
elif key == "q":
raise urwid.ExitMainLoop()
elif key == "r":
self.handle = self.loop.event_loop.alarm(0, self.fetchAll)
focus_widget, idx = self.feedView.get_focus()
itemId = focus_widget.attr_map[None][0]
self.articles = tui.fetcher.articles[itemId[13:]]
self.articleView.fill(self.articles)
tui = TUI.create()
tui.run()