From: Simon Tatham Date: Wed, 6 Dec 2023 07:30:03 +0000 (+0000) Subject: Now we can read 'messages'! (aka mentions) X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ian/git?a=commitdiff_plain;h=40fe7ea159670db4789f358b34f6f2672d088614;p=mastodonochrome.git Now we can read 'messages'! (aka mentions) --- diff --git a/client.py b/client.py index d46d60b..8e29ae0 100644 --- a/client.py +++ b/client.py @@ -150,6 +150,9 @@ class Client: def home_timeline_feed(self): return HomeTimelineFeed(self) + def mentions_feed(self): + return MentionsFeed(self) + class Feed: """Base class that encapsulates some kind of collection of _things_ we can get from the server, with both the ability to go backwards in @@ -172,7 +175,7 @@ class IncrementalServerFeed(Feed): def start(self): data, links = self.client.get_incremental_start( self.url, **self.params) - self.data = list(reversed(data)) + self.data = list(self.get(d) for d in reversed(data)) self.origin = len(self.data) self.prev_link = links['prev'] self.next_link = links['next'] @@ -191,7 +194,7 @@ class IncrementalServerFeed(Feed): data, links = self.client.get_incremental_cont(self.next_link) if len(data) == 0: return - self.data[0:0] = list(reversed(data)) + self.data[0:0] = list(self.get(d) for d in reversed(data)) self.origin += len(data) self.next_link = links['next'] @@ -201,7 +204,7 @@ class IncrementalServerFeed(Feed): data, links = self.client.get_incremental_cont(self.prev_link) if len(data) == 0: return - self.data.extend(reversed(data)) + self.data.extend(self.get(d) for d in reversed(data)) self.prev_link = links['prev'] class HomeTimelineFeed(IncrementalServerFeed): @@ -210,7 +213,8 @@ class HomeTimelineFeed(IncrementalServerFeed): class MentionsFeed(IncrementalServerFeed): def __init__(self, client): - super().__init__(client, "notifications", **{"types[]":['mention']}) + super().__init__(client, "notifications", {"types[]":['mention']}, + get=lambda item: item['status']) class Status: def __init__(self, data, client): diff --git a/cursesclient.py b/cursesclient.py index 564a7a0..5d2106a 100644 --- a/cursesclient.py +++ b/cursesclient.py @@ -140,44 +140,116 @@ class CursesUI(client.Client): def run(self): home_feed = self.home_timeline_feed() - self.add_streaming_selfpipe("streaming/user", home_feed.extend_future) - self.add_sigwinch_selfpipe() + mentions_feed = self.mentions_feed() + + def extend_both(): + home_feed.extend_future() + mentions_feed.extend_future() + self.add_streaming_selfpipe("streaming/user", extend_both) + self.home_timeline = StatusFile( self, home_feed, text.ColouredString("Home timeline ", "HHHHHHHHHHHHHHHHHKH")) + self.mentions_timeline = StatusFile( + self, mentions_feed, + text.ColouredString("Mentions [ESC][R]", + "HHHHHHHHHHHHKKKHHKH")) + + self.add_sigwinch_selfpipe() + + self.escape_menu = EscMenu(self) + + self.activity_stack = [self.home_timeline] try: self.curses_setup() while True: self.clear() - self.home_timeline.render() + self.activity_stack[-1].render() self.scr.refresh() ch = self.get_input() - if ch in {ord(' '), curses.KEY_NPAGE}: - self.home_timeline.down_screen() - elif ch in {ord('-'), ord('b'), ord('B'), curses.KEY_PPAGE}: - self.home_timeline.up_screen() - elif ch in {13, curses.KEY_DOWN}: - self.home_timeline.down_line() - elif ch in {curses.KEY_UP}: - self.home_timeline.up_line() - elif ch in {ord('0'), curses.KEY_HOME}: - self.home_timeline.goto_top() - elif ch in {ord('z'), ord('Z'), curses.KEY_END}: - self.home_timeline.goto_bottom() - elif ch in {ord('q'), ord('Q')}: - break + if ch == 27: + if self.activity_stack[-1] is not self.escape_menu: + self.activity_stack.append(self.escape_menu) elif ch == curses.KEY_RESIZE: self.scr_h, self.scr_w = self.scr.getmaxyx() + else: + if self.activity_stack[-1].handle_key(ch) == 'quit': + if len(self.activity_stack) > 1: + self.activity_stack.pop() + else: + # Exit whole program when activity stack empties. + # FIXME: do we want to do this, or should the Main + # Menu be sticky at the bottom of the stack and + # require a special ESC X X to exit? + return finally: self.curses_shutdown() -class StatusFile: - def __init__(self, cc, feed, title): +class Menu: + def __init__(self, cc): self.cc = cc + + def render(self): + y = 0 + header = text.FileHeader(self.title) + + for line in header.render(self.cc.scr_w): + self.cc.print_at(y, 0, line) + y += 1 + + sl = text.FileStatusLine() + sl_rendered = util.exactly_one(sl.render(self.cc.scr_w)) + self.cc.print_at(self.cc.scr_h - 1, 0, sl_rendered) + + def handle_key(self, ch): + if ch in {ord('q'), ord('Q'), 13}: + return 'quit' + +class EscMenu(Menu): + def __init__(self, cc): + super().__init__(cc) + self.title = text.ColouredString( + "Utilities [ESC]", + "HHHHHHHHHHHKKKH") + + def chain_to(self, activity): + assert self.cc.activity_stack[-1] is self + self.cc.activity_stack[-1] = activity + + def handle_key(self, ch): + if ch in {ord('r'), ord('R')}: + self.chain_to(self.cc.mentions_timeline) + else: + super().handle_key(ch) + +class File: + # Base class for anything where you page up and down. + def __init__(self, cc): + self.cc = cc + + def handle_key(self, ch): + if ch in {ord(' '), curses.KEY_NPAGE}: + self.down_screen() + elif ch in {ord('-'), ord('b'), ord('B'), curses.KEY_PPAGE}: + self.up_screen() + elif ch in {13, curses.KEY_DOWN}: + self.down_line() + elif ch in {curses.KEY_UP}: + self.up_line() + elif ch in {ord('0'), curses.KEY_HOME}: + self.goto_top() + elif ch in {ord('z'), ord('Z'), curses.KEY_END}: + self.goto_bottom() + elif ch in {ord('q'), ord('Q')}: + return 'quit' + +class StatusFile(File): + def __init__(self, cc, feed, title): + super().__init__(cc) self.feed = feed self.feed.start() diff --git a/text.py b/text.py index 5b514cb..fd39d4f 100644 --- a/text.py +++ b/text.py @@ -211,6 +211,8 @@ class FileStatusLine: message += sep + ColouredString('({:d}%)'.format( int(self.proportion * 100))) sep = sep2 + elif message.width != 0: + message += ColouredString('.') space = width - message.width lspace = space // 2 + 1