From dea614c5c8754b1357207e536b0b0adb308c5bf0 Mon Sep 17 00:00:00 2001 From: Felix Pankratz Date: Tue, 29 Aug 2023 20:09:52 +0200 Subject: [PATCH] refactor into object :) --- hn.py | 111 ++++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 69 insertions(+), 42 deletions(-) diff --git a/hn.py b/hn.py index 7957442..2267657 100755 --- a/hn.py +++ b/hn.py @@ -15,34 +15,45 @@ import api spinner_states = ['-', '\\', '|', '/'] -def footer(stdscr, content): - stdscr.addstr(curses.LINES-1, 0, content, curses.A_REVERSE) - - -def main(stdscr): - stdscr.clear() - - _, width = stdscr.getmaxyx(); - num_stories = curses.LINES - 3 # headline, detail, footer - - topstories = api.get_topstories() - stories = [] - for idx, i in enumerate(topstories[:num_stories]): - stdscr.clear() - footer(stdscr, f'[{spinner_states[idx%4]}] Loading stories...') - stdscr.refresh() - stories.append(api.get_story(i)) - - # Display list of stories in terminal window with arrow key navigation - current_pos = 0 - while True: - stdscr.clear() - stdscr.addstr('Hacker News Top Stories:\n') - for i, story in enumerate(stories): - prefix = '>>> ' if i == current_pos else ' ' +class Client: + def __init__(self): + # set up curses + self.screen = curses.initscr() + curses.noecho() + curses.cbreak() + self.screen.keypad(True) + + self.topstories = api.get_topstories() + self.loadedstories = [] + self.story_pos = 0 + self.cursor_pos = 0 + self.lines = curses.LINES + self.cols = curses.COLS + self.stories_in_a_site = self.lines - 3 + + def load_stories(self, from_pos, to_pos): + for idx, i in enumerate(self.topstories[from_pos:to_pos]): + #stdscr.clear() + self.set_footer(f'[{spinner_states[idx%4]}] Loading stories...') + self.screen.refresh() + self.loadedstories.append(api.get_story(i)) + + def set_footer(self, footer): + self.screen.addstr(curses.LINES - 1, 0, footer, curses.A_REVERSE) + + def draw(self): + self.screen.clear() + self.lines = curses.LINES + self.cols = curses.COLS + # header, detail, footer: + self.stories_in_a_site = self.lines - 3 + + self.screen.addstr('Hacker News Top Stories:\n') + for i, story in enumerate(self.loadedstories[self.story_pos:self.story_pos + self.stories_in_a_site]): + prefix = '>>> ' if i == self.cursor_pos else ' ' # calculate length of line text = f'{prefix} ()\n' - chars_available = width - len(text) + chars_available = self.cols - len(text) max_title_len = min((chars_available//3)*2, len(story.title)) max_url_len = chars_available - max_title_len @@ -51,27 +62,43 @@ def main(stdscr): link = link[:max_url_len-1] + "…" if len(link) > max_url_len else link text = '{}{} ({})\n'.format(prefix, title, link.replace('https://', '').replace('http://', '')) - stdscr.addstr(text) - if i == current_pos: + self.screen.addstr(text) + if i == self.cursor_pos: detail = f' by {story.author} | {story.comments} comments | {story.votes} points\n' - stdscr.addstr(detail) - footer(stdscr, f'Loaded {num_stories} stories.') + self.screen.addstr(detail) + self.set_footer(f'Loaded {self.stories_in_a_site} stories.') - stdscr.refresh() - c = stdscr.getch() + def handle_input(self): + c = self.screen.getch() if c == ord('q'): # Quit - break + self.exit() elif c == curses.KEY_UP: - current_pos -= 1 - if current_pos < 0: - current_pos = len(stories)-1 + self.cursor_pos -= 1 + if self.cursor_pos < 0: + self.cursor_pos = self.stories_in_a_site-1 elif c == curses.KEY_DOWN: - current_pos += 1 - if current_pos >= len(stories): - current_pos = 0 + self.cursor_pos += 1 + if self.cursor_pos >= self.stories_in_a_site: + self.cursor_pos = 0 elif c == ord('c'): - webbrowser.open(f'https://news.ycombinator.com/item?id={stories[current_pos].id}') + webbrowser.open(f'https://news.ycombinator.com/item?id={self.loadedstories[self.story_pos + self.cursor_pos].id}') elif c == curses.KEY_ENTER or c == 10: - webbrowser.open(stories[current_pos].link) + webbrowser.open(self.loadedstories[self.story_pos + self.cursor_pos].link) + + def run(self): + self.load_stories(0, self.stories_in_a_site) + while True: + self.draw() + self.handle_input() + + def exit(self): + curses.endwin() + import sys + sys.exit(0) + +def main(): + client = Client() + client.run() -curses.wrapper(main) +if __name__ == '__main__': + main()