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
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']
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']
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):
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):
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 <H>",
"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()