chiark / gitweb /
Feature to print detailed info about a status.
authorSimon Tatham <anakin@pobox.com>
Sat, 9 Dec 2023 15:44:00 +0000 (15:44 +0000)
committerSimon Tatham <anakin@pobox.com>
Sat, 9 Dec 2023 17:21:12 +0000 (17:21 +0000)
Now I can retrieve its URL to send to people, and find out all the
fiddly internals too.

client.py
cursesclient.py
text.py

index e366fb963d90965fc44a93480375853d2d1af9fe..eaecbd94526685215928925bc27f1f49215512dd 100644 (file)
--- a/client.py
+++ b/client.py
@@ -279,6 +279,22 @@ class ThreadFeed(Feed):
     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", {
@@ -291,8 +307,14 @@ def parse_creation_time(created_at):
     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']
@@ -302,6 +324,7 @@ class Status:
 
         client.cache_status(data)
 
+        self.data = data
         self.post_id = data['id']
 
         self.datestamp = parse_creation_time(data['created_at'])
@@ -323,6 +346,8 @@ class Status:
 
         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)
@@ -353,6 +378,49 @@ class Status:
         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:
index d068ed9350e4a58da16a7d6e554580845e02fc6c..64b6cf13123df2754bce89dc1db344c1feb1bffd 100644 (file)
@@ -456,6 +456,8 @@ class File(Activity):
                 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'
@@ -484,6 +486,9 @@ class File(Activity):
             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
@@ -616,6 +621,8 @@ class ObjectFile(File):
             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'))
@@ -629,6 +636,8 @@ class ObjectFile(File):
             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))
@@ -654,6 +663,11 @@ class ObjectFile(File):
             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):
@@ -721,6 +735,12 @@ class ObjectFile(File):
         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
@@ -762,6 +782,15 @@ class NotificationsFile(ObjectFile):
     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):
diff --git a/text.py b/text.py
index de68152b42b09068bb2af9026c4a05f30cc5af8a..36af7147cff356f9ce33f8f0171aeb4ef607591e 100644 (file)
--- a/text.py
+++ b/text.py
@@ -33,6 +33,7 @@ colourmap = {
     '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 = {}
@@ -56,6 +57,10 @@ class ColouredString:
         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)
 
@@ -121,17 +126,19 @@ class BlankLine:
         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') +
@@ -311,11 +318,15 @@ class MenuKeypressLine:
                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