diff --git a/toot/api.py b/toot/api.py index 1ce1ec0..17458e7 100644 --- a/toot/api.py +++ b/toot/api.py @@ -260,6 +260,12 @@ def tag_timeline_generator(app, user, hashtag, local=False, limit=20): return _timeline_generator(app, user, path, params) +def bookmark_timeline_generator(app, user, limit=20): + path = '/api/v1/bookmarks' + params = {'limit': limit} + return _timeline_generator(app, user, path, params) + + def timeline_list_generator(app, user, list_id, limit=20): path = '/api/v1/timelines/list/{}'.format(list_id) return _timeline_generator(app, user, path, {'limit': limit}) diff --git a/toot/tui/app.py b/toot/tui/app.py index 62ea2c3..2dd53dc 100644 --- a/toot/tui/app.py +++ b/toot/tui/app.py @@ -196,6 +196,7 @@ class TUI(urwid.Frame): def _zoom(timeline, status_details): self.show_status_zoom(status_details) + urwid.connect_signal(timeline, "bookmark", self.async_toggle_bookmark) urwid.connect_signal(timeline, "compose", _compose) urwid.connect_signal(timeline, "delete", _delete) urwid.connect_signal(timeline, "favourite", self.async_toggle_favourite) @@ -390,12 +391,15 @@ class TUI(urwid.Frame): lambda x: self.goto_home_timeline()) urwid.connect_signal(menu, "public_timeline", lambda x, local: self.goto_public_timeline(local)) + urwid.connect_signal(menu, "bookmark_timeline", + lambda x, local: self.goto_bookmarks()) + urwid.connect_signal(menu, "hashtag_timeline", lambda x, tag, local: self.goto_tag_timeline(tag, local=local)) self.open_overlay(menu, title="Go to", options=dict( align="center", width=("relative", 60), - valign="middle", height=9 + len(user_timelines), + valign="middle", height=10 + len(user_timelines), )) def show_help(self): @@ -413,6 +417,12 @@ class TUI(urwid.Frame): promise = self.async_load_timeline(is_initial=True, timeline_name="public") promise.add_done_callback(lambda *args: self.close_overlay()) + def goto_bookmarks(self): + self.timeline_generator = api.bookmark_timeline_generator( + self.app, self.user, limit=40) + promise = self.async_load_timeline(is_initial=True, timeline_name="bookmarks") + promise.add_done_callback(lambda *args: self.close_overlay()) + def goto_tag_timeline(self, tag, local): self.timeline_generator = api.tag_timeline_generator( self.app, self.user, tag, local=local, limit=40) @@ -542,6 +552,27 @@ class TUI(urwid.Frame): else: self.run_in_thread(_translate, done_callback=_done) + def async_toggle_bookmark(self, timeline, status): + def _bookmark(): + logger.info("Bookmarking {}".format(status)) + api.bookmark(self.app, self.user, status.id) + + def _unbookmark(): + logger.info("Unbookmarking {}".format(status)) + api.unbookmark(self.app, self.user, status.id) + + def _done(loop): + # Create a new Status with flipped bookmarked flag + new_data = status.data + new_data["bookmarked"] = not status.bookmarked + new_status = self.make_status(new_data) + timeline.update_status(new_status) + + self.run_in_thread( + _unbookmark if status.bookmarked else _bookmark, + done_callback=_done + ) + def async_delete_status(self, timeline, status): def _delete(): api.delete_status(self.app, self.user, status.id) diff --git a/toot/tui/constants.py b/toot/tui/constants.py index e2a2a5c..563eb8e 100644 --- a/toot/tui/constants.py +++ b/toot/tui/constants.py @@ -34,6 +34,7 @@ PALETTE = [ ('green_selected', 'white,bold', 'dark green'), ('yellow', 'yellow', ''), ('yellow_bold', 'yellow,bold', ''), + ('red', 'dark red', ''), ('warning', 'light red', ''), ] diff --git a/toot/tui/entities.py b/toot/tui/entities.py index 082e664..a30bcb6 100644 --- a/toot/tui/entities.py +++ b/toot/tui/entities.py @@ -56,6 +56,7 @@ class Status: self.author = self._get_author() self.favourited = data.get("favourited", False) self.reblogged = data.get("reblogged", False) + self.bookmarked = data.get("bookmarked", False) self.in_reply_to = data.get("in_reply_to_id") self.url = data.get("url") self.mentions = data.get("mentions") diff --git a/toot/tui/overlays.py b/toot/tui/overlays.py index 51ad22f..9af13ce 100644 --- a/toot/tui/overlays.py +++ b/toot/tui/overlays.py @@ -45,7 +45,7 @@ class StatusLinks(urwid.ListBox): class ExceptionStackTrace(urwid.ListBox): """Shows an exception stack trace.""" def __init__(self, ex): - lines = traceback.format_exception(etype=type(ex), value=ex, tb=ex.__traceback__) + lines = traceback.format_exception(type(ex), value=ex, tb=ex.__traceback__) walker = urwid.SimpleFocusListWalker([ urwid.Text(line) for line in lines ]) @@ -74,6 +74,7 @@ class GotoMenu(urwid.ListBox): "home_timeline", "public_timeline", "hashtag_timeline", + "bookmark_timeline", ] def __init__(self, user_timelines): @@ -96,6 +97,9 @@ class GotoMenu(urwid.ListBox): def _global_public(button): self._emit("public_timeline", False) + def _bookmarks(button): + self._emit("bookmark_timeline", False) + def _hashtag(local): hashtag = self.get_hashtag() if hashtag: @@ -117,6 +121,7 @@ class GotoMenu(urwid.ListBox): yield Button("Local public timeline", on_press=_local_public) yield Button("Global public timeline", on_press=_global_public) + yield Button("Bookmarks", on_press=_bookmarks) yield urwid.Divider() yield self.hash_edit yield Button("Local hashtag timeline", on_press=lambda x: _hashtag(True)) @@ -164,6 +169,7 @@ class Help(urwid.Padding): yield urwid.Text(h(" [B] - Boost/unboost status")) yield urwid.Text(h(" [C] - Compose new status")) yield urwid.Text(h(" [F] - Favourite/unfavourite status")) + yield urwid.Text(h(" [K] - Bookmark/unbookmark status")) yield urwid.Text(h(" [N] - Translate status if possible (toggle)")) yield urwid.Text(h(" [R] - Reply to current status")) yield urwid.Text(h(" [S] - Show text marked as sensitive")) diff --git a/toot/tui/timeline.py b/toot/tui/timeline.py index e912fe1..2521c7a 100644 --- a/toot/tui/timeline.py +++ b/toot/tui/timeline.py @@ -21,6 +21,7 @@ class Timeline(urwid.Columns): "delete", # Delete own status "favourite", # Favourite status "focus", # Focus changed + "bookmark", # Bookmark status "media", # Display media attachments "menu", # Show a context menu "next", # Fetch more statuses @@ -67,6 +68,7 @@ class Timeline(urwid.Columns): "green": "green_selected", "yellow": "green_selected", "cyan": "green_selected", + "red": "green_selected", None: "green_selected", }) @@ -156,6 +158,10 @@ class Timeline(urwid.Columns): self.refresh_status_details() return + if key in ("k", "K"): + self._emit("bookmark", status) + return + if key in ("l", "L"): self._emit("links", status) return @@ -308,6 +314,7 @@ class StatusDetails(urwid.Pile): ) yield ("pack", urwid.Text([ + ("red" if status.bookmarked else "gray", "🠷" if status.bookmarked else " "), ("gray", f"⤶ {status.data['replies_count']} "), ("yellow" if status.reblogged else "gray", f"♺ {status.data['reblogs_count']} "), ("yellow" if status.favourited else "gray", f"★ {status.data['favourites_count']}"), @@ -322,6 +329,7 @@ class StatusDetails(urwid.Pile): "[B]oost", "[D]elete" if status.is_mine else "", "[F]avourite", + "Boo[k]mark", "[V]iew", "[T]hread" if not self.in_thread else "", "[L]inks",