def __getitem__(self, n):
return self.data[n]
+class StatusInfoFeed(Feed):
+ def __init__(self, client, status):
+ self.client = client
+ self.status = status
+ self.data = [Status(status, client, full_info=True)]
+
+ def start(self):
+ pass
+
+ def min_index(self):
+ return 0
+ def max_index(self):
+ return len(self.data)
+ def __getitem__(self, n):
+ return self.data[n]
+
class EgoFeed(IncrementalServerFeed):
def __init__(self, client):
super().__init__(client, "notifications", {
tm = time.strptime(date, "%Y-%m-%dT%H:%M:%S")
return calendar.timegm(tm)
+def noneify(s, include_empty=False, colour=' '):
+ if include_empty and s is not None and len(s) == 0:
+ s = None # empty string counts as None
+ return (text.ColouredString("none", '0') if s is None
+ else text.ColouredString(s, colour))
+
class Status:
- def __init__(self, data, client):
+ def __init__(self, data, client, full_info=False):
rb = data.get('reblog')
if rb is not None:
self.booster = data['account']
client.cache_status(data)
+ self.data = data
self.post_id = data['id']
self.datestamp = parse_creation_time(data['created_at'])
self.update_fave_boost(data)
+ self.full_info = full_info
+
def update_fave_boost(self, data):
self.favourited = data.get('favourited', False)
self.boosted = data.get('boosted', False)
for media in self.media:
yield text.Media(media['url'], media.get('description'))
+ if self.full_info:
+ yield text.SeparatorLine()
+ yield text.BlankLine()
+ yield text.Paragraph(f"Post id: " + self.post_id)
+ url = self.data['url']
+ yield text.Paragraph("On the web: " +
+ text.ColouredString(url, 'u'))
+ yield text.BlankLine()
+
+ created = noneify(self.data['created_at'])
+ yield text.Paragraph(f"Creation time: " + created)
+ edited = noneify(self.data['edited_at'])
+ yield text.Paragraph(f"Last edit time: " + edited)
+ reply_id = noneify(self.data.get('in_reply_to_id'))
+ yield text.Paragraph("Reply to post: " + reply_id)
+ reply_acct = noneify(self.data.get('in_reply_to_account_id'))
+ yield text.Paragraph("Reply to account: " + reply_acct)
+ yield text.BlankLine()
+
+ lang = noneify(self.data['language'])
+ yield text.Paragraph("Language: " + lang)
+ vis = self.data['visibility']
+ yield text.Paragraph("Visibility: " + vis)
+ sensitive = "yes" if self.data['sensitive'] else "no"
+ yield text.Paragraph("Sensitive: " + sensitive)
+ spoiler = noneify(self.data['spoiler_text'], include_empty=True)
+ yield text.Paragraph("Spoiler text: " + spoiler)
+ yield text.BlankLine()
+
+ replies = str(self.data['replies_count'])
+ yield text.Paragraph("Replies: " + replies)
+ boosts = str(self.data['reblogs_count'])
+ yield text.Paragraph("Boosts: " + boosts)
+ faves = str(self.data['favourites_count'])
+ yield text.Paragraph("Favourites: " + faves)
+ yield text.BlankLine()
+
+ app_subdict = self.data.get('application', {})
+ client = noneify(app_subdict.get('name'))
+ yield text.Paragraph("Client name: " + client)
+ client_url = noneify(app_subdict.get('website'), colour='u')
+ yield text.Paragraph("Client website: " + client_url)
+
def get_reply_recipients(self):
yield self.client.fq(self.account['acct'])
for mention in self.mentions:
self.boost_mode()
elif ch in {'t', 'T'}:
self.thread_mode()
+ elif ch in {'i', 'I'}:
+ self.info_mode()
elif self.mode == 'select':
if ch in {'q', 'Q'}:
self.mode = 'normal'
elif (self.select_type == 'thread' and
ch in {'f', 'F'}):
self.thread_complete(True)
+ elif (self.select_type == 'info' and
+ ch in {' '}):
+ self.info_complete(False)
def unprime(self):
pass # not supported
elif self.select_type == 'thread':
sl.keys.append(('SPACE', 'Show Thread Context'))
sl.keys.append(('F', 'Show Full Thread'))
+ elif self.select_type == 'info':
+ sl.keys.append(('SPACE', 'Show Post Info'))
sl.keys.append(('-', None))
sl.keys.append(('+', None))
sl.keys.append(('Q', 'Quit'))
if self.items_are_statuses:
sl.keys.append(('F', 'Fave'))
sl.keys.append(('^B', 'Boost'))
+ # FIXME: for when we can auto-shink bottom line
+ # sl.keys.append(('I', 'Info'))
sl.keys.append(('Q', 'Exit'))
sl.proportion = self.linepos / len(self.lines)
sl_rendered = util.exactly_one(sl.render(self.cc.scr_w))
self.mode = 'select'
self.select_type = 'thread'
self.select_target = self.index_by_line[self.linepos-1]
+ def info_mode(self):
+ if self.items_are_statuses:
+ self.mode = 'select'
+ self.select_type = 'info'
+ self.select_target = self.index_by_line[self.linepos-1]
def prev_select_target(self):
self.select_target = max(self.minpos, self.select_target-1)
def next_select_target(self):
self.push_to(StatusFile(
self.cc, feed, text.ColouredString(title, "H")))
+ def info_complete(self, full):
+ self.mode = 'normal'
+ target = self.statuses[self.select_target]
+ reply_id = target.get_reply_id()
+ self.push_to(StatusInfoFile(self.cc, reply_id))
+
def move_to(self, pos):
old_linepos = self.linepos
self.linepos = pos
def __init__(self, cc, feed, title):
super().__init__(cc, client.Notification, feed, title)
+class StatusInfoFile(ObjectFile):
+ items_are_statuses = True
+ items_have_authors = True
+ def __init__(self, cc, postid):
+ title = text.ColouredString(f"Information about post {postid}", 'H')
+ self.data = cc.get_status_by_id(postid)
+ super().__init__(
+ cc, lambda x,cc:x, client.StatusInfoFeed(cc, self.data), title)
+
class Composer(Activity):
class DisplayText:
def __init__(self, text):
'K': [0, 1, 36], # keypress / keypath names in file headers
'k': [0, 1], # keypresses in file status lines
'-': [0, 7, 40, 36], # separator line between editor header and content
+ '0': [0, 34], # something really boring, like 'none' in place of data
}
wcswidth_cache = {}
rhs = type(self)(rhs)
return type(self)(self.s + rhs.s, self.c + rhs.c)
+ def __radd__(self, lhs):
+ lhs = type(self)(lhs)
+ return type(self)(lhs.s + self.s, lhs.c + self.c)
+
def __mul__(self, rhs):
return type(self)(self.s * rhs, self.c * rhs)
yield ColouredString("")
class SeparatorLine:
- def __init__(self, timestamp, favourited=False, boosted=False):
+ def __init__(self, timestamp=None, favourited=False, boosted=False):
self.timestamp = timestamp
self.favourited = favourited
self.boosted = boosted
def render(self, width):
- date = time.strftime("%a %b %e %H:%M:%S %Y",
- time.localtime(self.timestamp))
- suffix = (ColouredString("[", 'S') +
- ColouredString(date, 'D') +
- ColouredString("]--", 'S'))
+ suffix = ColouredString("")
+ if self.timestamp is not None:
+ date = time.strftime("%a %b %e %H:%M:%S %Y",
+ time.localtime(self.timestamp))
+ suffix = (ColouredString("[", 'S') +
+ ColouredString(date, 'D') +
+ ColouredString("]--", 'S')) + suffix
if self.boosted:
suffix = (ColouredString("[", 'S') +
ColouredString('B', 'D') +
self.description)
class Paragraph:
- def __init__(self):
+ def __init__(self, text=None):
self.words = []
self.space_colours = []
self.unfinished_word = ColouredString('')
+ if text is not None:
+ self.add(text)
+ self.end_word()
+
def render(self, width, laterwidth=None):
if laterwidth is None:
laterwidth = width