From 6f0514fd6a28982bad63196e5a00c75352737ea7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Pav=C3=B3n=20Abi=C3=A1n?= Date: Sun, 1 Feb 2026 20:40:09 +0100 Subject: [PATCH] Refactor y eliminar patrones raros. --- src/controller/blueski/messages.py | 18 ++ src/controller/buffers/blueski/base.py | 133 ++++++++------ .../controller/buffers/mastodon/__init__.py | 1 + .../buffers/mastodon/announcements.py | 165 ++++++++++++++++++ .../controller/buffers/mastodon/base.py | 46 ++++- .../controller/buffers/mastodon/community.py | 16 +- .../controller/buffers/mastodon/mentions.py | 20 ++- .../controller/buffers/mastodon/search.py | 5 +- srcantiguo/controller/mastodon/handler.py | 3 + srcantiguo/controller/mastodon/messages.py | 7 +- srcantiguo/controller/mastodon/settings.py | 14 +- .../controller/mastodon/templateEditor.py | 4 +- srcantiguo/keymaps/Chicken Nugget.keymap | 12 +- srcantiguo/keymaps/Qwitter.keymap | 12 +- srcantiguo/keymaps/Windows 10.keymap | 12 +- srcantiguo/keymaps/Windows11.keymap | 12 +- srcantiguo/keymaps/base.template | 11 +- srcantiguo/keymaps/default.keymap | 12 +- .../keystrokeEditor/actions/mastodon.py | 12 +- srcantiguo/mastodon.defaults | 3 +- srcantiguo/sessions/mastodon/compose.py | 8 +- srcantiguo/sessions/mastodon/session.py | 65 +++++-- srcantiguo/sessions/mastodon/templates.py | 22 +++ srcantiguo/setup.py | 6 +- srcantiguo/wxUI/buffers/mastodon/__init__.py | 3 +- .../wxUI/buffers/mastodon/announcements.py | 36 ++++ .../wxUI/dialogs/mastodon/configuration.py | 8 +- srcantiguo/wxUI/dialogs/mastodon/dialogs.py | 40 ++++- srcantiguo/wxUI/dialogs/mastodon/menus.py | 4 + 29 files changed, 583 insertions(+), 127 deletions(-) create mode 100644 srcantiguo/controller/buffers/mastodon/announcements.py create mode 100644 srcantiguo/wxUI/buffers/mastodon/announcements.py diff --git a/src/controller/blueski/messages.py b/src/controller/blueski/messages.py index cb81eaee..56a60e63 100644 --- a/src/controller/blueski/messages.py +++ b/src/controller/blueski/messages.py @@ -98,6 +98,24 @@ def format_error_message(error_description: str, details: str | None = None) -> logger.info("Blueski messages module loaded (placeholders).") +class post(base_messages.basicMessage): + def __init__(self, session: Any, title: str, caption: str, text: str = "", *args, **kwargs): + self.session = session + self.title = title + self.message = postDialogs.Post(caption=caption, text=text, *args, **kwargs) + try: + self.message.SetTitle(title) + self.message.text.SetInsertionPoint(len(self.message.text.GetValue())) + except Exception: + pass + + def get_data(self): + return self.message.get_payload() + + def text_processor(self): + pass + + def _g(obj: Any, key: str, default: Any = None) -> Any: if isinstance(obj, dict): return obj.get(key, default) diff --git a/src/controller/buffers/blueski/base.py b/src/controller/buffers/blueski/base.py index 474557c6..92b25b95 100644 --- a/src/controller/buffers/blueski/base.py +++ b/src/controller/buffers/blueski/base.py @@ -190,28 +190,20 @@ class BaseBuffer(base.Buffer): pub.sendMessage("execute-action", action="copy_to_clipboard") def on_post(self, evt): - from wxUI.dialogs.blueski import postDialogs - dlg = postDialogs.Post(caption=_("New Post")) - if dlg.ShowModal() == wx.ID_OK: - text, files, cw, langs = dlg.get_payload() - if not text and not files: - dlg.Destroy() - return - def do_send(): - try: - uri = self.session.send_message(message=text, files=files, cw_text=cw, langs=langs) - if uri: - wx.CallAfter(self.session.sound.play, "tweet_send.ogg") - wx.CallAfter(output.speak, _("Sent.")) - if hasattr(self, "start_stream"): - wx.CallAfter(self.start_stream, False, False) - else: - wx.CallAfter(output.speak, _("Failed to send post."), True) - except Exception: - log.exception("Error sending Bluesky post") - wx.CallAfter(output.speak, _("An error occurred while posting."), True) - call_threaded(do_send) - dlg.Destroy() + dlg = blueski_messages.post(session=self.session, title=_("New Post"), caption=_("New Post")) + if dlg.message.ShowModal() == wx.ID_OK: + text, files, cw, langs = dlg.get_data() + self._send_post_async( + text=text, + files=files, + cw_text=cw, + langs=langs, + success_message=_("Sent."), + error_message=_("An error occurred while posting."), + sound="tweet_send.ogg", + refresh_args=(False, False), + ) + dlg.message.Destroy() def on_reply(self, evt): item = self.get_item() @@ -237,38 +229,71 @@ class BaseBuffer(base.Buffer): handle = g(author, "handle", "") initial_text = f"@{handle} " if handle and not handle.startswith("@") else (f"{handle} " if handle else "") - from wxUI.dialogs.blueski import postDialogs - dlg = postDialogs.Post(caption=_("Reply"), text=initial_text) - if dlg.ShowModal() == wx.ID_OK: - text, files, cw, langs = dlg.get_payload() - if not text and not files: - dlg.Destroy() - return - def do_send(): - try: - uri_resp = self.session.send_message( - message=text, - files=files, - reply_to=uri, - reply_to_cid=reply_cid, - cw_text=cw, - langs=langs - ) - if uri_resp: - wx.CallAfter(self.session.sound.play, "reply_send.ogg") - wx.CallAfter(output.speak, _("Reply sent.")) - if getattr(self, "type", "") == "conversation": - try: - wx.CallAfter(self.start_stream, True, False) - except Exception: - pass - else: - wx.CallAfter(output.speak, _("Failed to send reply."), True) - except Exception: - log.exception("Error sending Bluesky reply") - wx.CallAfter(output.speak, _("An error occurred while replying."), True) - call_threaded(do_send) - dlg.Destroy() + dlg = blueski_messages.post(session=self.session, title=_("Reply"), caption=_("Reply"), text=initial_text) + if dlg.message.ShowModal() == wx.ID_OK: + text, files, cw, langs = dlg.get_data() + refresh_args = (True, False) if getattr(self, "type", "") == "conversation" else None + self._send_post_async( + text=text, + files=files, + cw_text=cw, + langs=langs, + reply_to=uri, + reply_to_cid=reply_cid, + success_message=_("Reply sent."), + error_message=_("An error occurred while replying."), + sound="reply_send.ogg", + refresh_args=refresh_args, + ) + dlg.message.Destroy() + + def _send_post_async( + self, + *, + text, + files, + cw_text, + langs, + reply_to=None, + reply_to_cid=None, + success_message="", + error_message="", + sound=None, + refresh_args=None, + ): + if not text and not files: + return + + def do_send(): + try: + uri_resp = self.session.send_message( + message=text, + files=files, + reply_to=reply_to, + reply_to_cid=reply_to_cid, + cw_text=cw_text, + langs=langs, + ) + if uri_resp: + if sound: + wx.CallAfter(self.session.sound.play, sound) + if success_message: + wx.CallAfter(output.speak, success_message) + if refresh_args and hasattr(self, "start_stream"): + try: + wx.CallAfter(self.start_stream, *refresh_args) + except Exception: + pass + else: + wx.CallAfter(output.speak, _("Failed to send post."), True) + except Exception: + log.exception("Error sending Bluesky post") + if error_message: + wx.CallAfter(output.speak, error_message, True) + else: + wx.CallAfter(output.speak, _("An error occurred while posting."), True) + + call_threaded(do_send) def on_repost(self, evt): self.share_item(confirm=True) diff --git a/srcantiguo/controller/buffers/mastodon/__init__.py b/srcantiguo/controller/buffers/mastodon/__init__.py index 0df3110d..9486c467 100644 --- a/srcantiguo/controller/buffers/mastodon/__init__.py +++ b/srcantiguo/controller/buffers/mastodon/__init__.py @@ -6,3 +6,4 @@ from .users import UserBuffer from .notifications import NotificationsBuffer from .search import SearchBuffer from .community import CommunityBuffer +from .announcements import AnnouncementsBuffer \ No newline at end of file diff --git a/srcantiguo/controller/buffers/mastodon/announcements.py b/srcantiguo/controller/buffers/mastodon/announcements.py new file mode 100644 index 00000000..a3ffbd54 --- /dev/null +++ b/srcantiguo/controller/buffers/mastodon/announcements.py @@ -0,0 +1,165 @@ +# -*- coding: utf-8 -*- +import time +import logging +import arrow +import widgetUtils +import wx +import output +import languageHandler +import config +from pubsub import pub +from controller.buffers.mastodon.base import BaseBuffer +from sessions.mastodon import compose, templates +from wxUI import buffers +from wxUI.dialogs.mastodon import menus +from mysc.thread_utils import call_threaded + +log = logging.getLogger("controller.buffers.mastodon.announcements") + +class AnnouncementsBuffer(BaseBuffer): + + def __init__(self, *args, **kwargs): + # We enforce compose_func="compose_announcement" + kwargs["compose_func"] = "compose_announcement" + super(AnnouncementsBuffer, self).__init__(*args, **kwargs) + self.type = "announcementsBuffer" + + def create_buffer(self, parent, name): + self.buffer = buffers.mastodon.announcementsPanel(parent, name) + + def get_buffer_name(self): + return _("Announcements") + + def bind_events(self): + self.buffer.set_focus_function(self.onFocus) + widgetUtils.connect_event(self.buffer.list.list, widgetUtils.KEYPRESS, self.get_event) + widgetUtils.connect_event(self.buffer, widgetUtils.BUTTON_PRESSED, self.dismiss_announcement, self.buffer.dismiss) + widgetUtils.connect_event(self.buffer.list.list, wx.EVT_LIST_ITEM_RIGHT_CLICK, self.show_menu) + widgetUtils.connect_event(self.buffer.list.list, wx.EVT_LIST_KEY_DOWN, self.show_menu_by_key) + + def dismiss_announcement(self, event=None, item=None, *args, **kwargs): + index = self.buffer.list.get_selected() + if index == -1: return + item = self.session.db[self.name][index] + + # Optimistic UI update or wait for API? Let's wait for API to be safe, but run threaded. + # We need a custom call because 'announcements_dismiss' returns None on success usually. + def _do_dismiss(): + try: + self.session.api_call(call_name="announcements_dismiss", id=str(item.id)) + # If success, update UI in main thread + wx.CallAfter(self._on_dismiss_success, index) + except Exception as e: + log.exception("Error dismissing announcement") + self.session.sound.play("error.ogg") + + call_threaded(_do_dismiss) + + def _on_dismiss_success(self, index): + if index < len(self.session.db[self.name]): + self.session.db[self.name].pop(index) + self.buffer.list.remove_item(index) + output.speak(_("Announcement dismissed.")) + + def show_menu(self, ev, pos=0, *args, **kwargs): + if self.buffer.list.get_count() == 0: + return + # Create a simple menu + menu = wx.Menu() + dismiss_item = menu.Append(wx.ID_ANY, _("Dismiss")) + copy_item = menu.Append(wx.ID_ANY, _("Copy text")) + + self.buffer.Bind(wx.EVT_MENU, self.dismiss_announcement, dismiss_item) + self.buffer.Bind(wx.EVT_MENU, self.copy, copy_item) + + if pos != 0: + self.buffer.PopupMenu(menu, pos) + else: + self.buffer.PopupMenu(menu, self.buffer.list.list.GetPosition()) + + def url(self, *args, **kwargs): + self.dismiss_announcement() + + def get_more_items(self): output.speak(_("This buffer does not support loading more items."), True) + + # Disable social interactions not applicable to announcements + def reply(self, *args, **kwargs): + pass + + def share_item(self, *args, **kwargs): + pass + + def toggle_favorite(self, *args, **kwargs): + pass + + def add_to_favorites(self, *args, **kwargs): + pass + + def remove_from_favorites(self, *args, **kwargs): + pass + + def toggle_bookmark(self, *args, **kwargs): + pass + + def mute_conversation(self, *args, **kwargs): + pass + + def vote(self, *args, **kwargs): + pass + + def send_message(self, *args, **kwargs): + pass + + def user_details(self, *args, **kwargs): + pass + + def view_item(self, *args, **kwargs): + # We could implement a specific viewer for announcements if needed, + # but the default one expects a status object structure. + pass + + def copy(self, event=None): + item = self.get_item() + if item: + pub.sendMessage("execute-action", action="copy_to_clipboard") + + def onFocus(self, *args, **kwargs): + # Similar logic to BaseBuffer but adapted if needed. + # BaseBuffer.onFocus handles reading long posts. + if config.app["app-settings"]["read_long_posts_in_gui"] == True and self.buffer.list.list.HasFocus(): + wx.CallLater(40, output.speak, self.get_message(), interrupt=True) + + def get_message(self): + # Override to use announcement template + announcement = self.get_item() + if announcement == None: + return + template = self.session.settings.get("templates", {}).get("announcement", templates.announcement_default_template) + t = templates.render_announcement(announcement, template, self.session.settings, relative_times=self.session.settings["general"]["relative_times"], offset_hours=self.session.db["utc_offset"]) + return t + + def start_stream(self, mandatory=False, play_sound=True, avoid_autoreading=False): + current_time = time.time() + if self.execution_time == 0 or current_time-self.execution_time >= 300 or mandatory==True: + self.execution_time = current_time + log.debug("Starting stream for announcements buffer") + try: + # The announcements API does not accept min_id or limit parameters + results = self.session.api.announcements() + # Reverse the list so order_buffer processes them according to user preference + results.reverse() + except Exception as e: + log.exception("Error retrieving announcements: %s" % (str(e))) + return 0 + + # order_buffer handles duplication filtering by ID internally + number_of_items = self.session.order_buffer(self.name, results) + log.debug("Number of new announcements retrieved: %d" % (number_of_items,)) + + self.put_items_on_list(number_of_items) + + if number_of_items > 0 and play_sound == True and self.sound != None and self.session.settings["sound"]["session_mute"] == False: + self.session.sound.play(self.sound) + + return number_of_items + return 0 diff --git a/srcantiguo/controller/buffers/mastodon/base.py b/srcantiguo/controller/buffers/mastodon/base.py index 1595f932..85ac3bf8 100644 --- a/srcantiguo/controller/buffers/mastodon/base.py +++ b/srcantiguo/controller/buffers/mastodon/base.py @@ -126,14 +126,16 @@ class BaseBuffer(base.Buffer): min_id = None # toDo: Implement reverse timelines properly here. if (self.name != "favorites" and self.name != "bookmarks") and self.name in self.session.db and len(self.session.db[self.name]) > 0: - if self.session.settings["general"]["reverse_timelines"]: - min_id = self.session.db[self.name][0].id - else: - min_id = self.session.db[self.name][-1].id + # We use the maximum ID present in the buffer to ensure we only request posts + # that are newer than our most recent chronological post. + # This prevents old pinned posts from pulling in hundreds of previous statuses. + min_id = max(item.id for item in self.session.db[self.name]) # loads pinned posts from user accounts. # Load those posts only when there are no items previously loaded. if "-timeline" in self.name and "account_statuses" in self.function and len(self.session.db.get(self.name, [])) == 0: pinned_posts = self.session.api.account_statuses(pinned=True, limit=count, *self.args, **self.kwargs) + for p in pinned_posts: + p["pinned"] = True pinned_posts.reverse() else: pinned_posts = None @@ -182,10 +184,17 @@ class BaseBuffer(base.Buffer): def get_more_items(self): elements = [] - if self.session.settings["general"]["reverse_timelines"] == False: - max_id = self.session.db[self.name][0].id + if len(self.session.db[self.name]) == 0: + return + # We use the minimum ID in the buffer to correctly request the next page of older items. + # This prevents old pinned posts from causing us to skip chronological posts. + # We try to exclude pinned posts from this calculation as they are usually outliers at the top. + unpinned_ids = [item.id for item in self.session.db[self.name] if not getattr(item, "pinned", False)] + if unpinned_ids: + max_id = min(unpinned_ids) else: - max_id = self.session.db[self.name][-1].id + max_id = min(item.id for item in self.session.db[self.name]) + try: items = getattr(self.session.api, self.function)(max_id=max_id, limit=self.session.settings["general"]["max_posts_per_call"], *self.args, **self.kwargs) except Exception as e: @@ -311,8 +320,10 @@ class BaseBuffer(base.Buffer): widgetUtils.connect_event(menu, widgetUtils.MENU, self.user_actions, menuitem=menu.userActions) if self.can_share() == True: widgetUtils.connect_event(menu, widgetUtils.MENU, self.share_item, menuitem=menu.boost) + widgetUtils.connect_event(menu, widgetUtils.MENU, self.quote, menuitem=menu.quote) else: menu.boost.Enable(False) + menu.quote.Enable(False) widgetUtils.connect_event(menu, widgetUtils.MENU, self.fav, menuitem=menu.fav) widgetUtils.connect_event(menu, widgetUtils.MENU, self.unfav, menuitem=menu.unfav) widgetUtils.connect_event(menu, widgetUtils.MENU, self.mute_conversation, menuitem=menu.mute) @@ -442,11 +453,30 @@ class BaseBuffer(base.Buffer): id = item.id if self.session.settings["general"]["boost_mode"] == "ask": answer = mastodon_dialogs.boost_question() - if answer == True: + if answer == 1: self._direct_boost(id) + elif answer == 2: + self.quote(item=item) else: self._direct_boost(id) + def quote(self, event=None, item=None, *args, **kwargs): + if item == None: + item = self.get_item() + if self.can_share(item=item) == False: + return output.speak(_("This action is not supported on conversations.")) + + title = _("Quote post") + caption = _("Write your comment here") + post = messages.post(session=self.session, title=title, caption=caption) + + response = post.message.ShowModal() + if response == wx.ID_OK: + post_data = post.get_data() + call_threaded(self.session.send_post, quote_id=item.id, posts=post_data, visibility=post.get_visibility(), language=post.get_language(), **kwargs) + if hasattr(post.message, "destroy"): + post.message.destroy() + def _direct_boost(self, id): item = self.session.api_call(call_name="status_reblog", _sound="retweet_send.ogg", id=id) diff --git a/srcantiguo/controller/buffers/mastodon/community.py b/srcantiguo/controller/buffers/mastodon/community.py index d0223431..aa319274 100644 --- a/srcantiguo/controller/buffers/mastodon/community.py +++ b/srcantiguo/controller/buffers/mastodon/community.py @@ -33,10 +33,7 @@ class CommunityBuffer(base.BaseBuffer): min_id = None # toDo: Implement reverse timelines properly here. if self.name in self.session.db and len(self.session.db[self.name]) > 0: - if self.session.settings["general"]["reverse_timelines"]: - min_id = self.session.db[self.name][0].id - else: - min_id = self.session.db[self.name][-1].id + min_id = max(item.id for item in self.session.db[self.name]) try: results = self.community_api.timeline(timeline=self.timeline, min_id=min_id, limit=count, *self.args, **self.kwargs) results.reverse() @@ -55,10 +52,15 @@ class CommunityBuffer(base.BaseBuffer): def get_more_items(self): elements = [] - if self.session.settings["general"]["reverse_timelines"] == False: - max_id = self.session.db[self.name][0].id + if len(self.session.db[self.name]) == 0: + return + + unpinned_ids = [item.id for item in self.session.db[self.name] if not getattr(item, "pinned", False)] + if unpinned_ids: + max_id = min(unpinned_ids) else: - max_id = self.session.db[self.name][-1].id + max_id = min(item.id for item in self.session.db[self.name]) + try: items = self.community_api.timeline(timeline=self.timeline, max_id=max_id, limit=self.session.settings["general"]["max_posts_per_call"], *self.args, **self.kwargs) except Exception as e: diff --git a/srcantiguo/controller/buffers/mastodon/mentions.py b/srcantiguo/controller/buffers/mastodon/mentions.py index 8a3d397c..db5f4a0c 100644 --- a/srcantiguo/controller/buffers/mastodon/mentions.py +++ b/srcantiguo/controller/buffers/mastodon/mentions.py @@ -42,10 +42,22 @@ class MentionsBuffer(BaseBuffer): def get_more_items(self): elements = [] - if self.session.settings["general"]["reverse_timelines"] == False: - max_id = self.session.db[self.name][0].id - else: - max_id = self.session.db[self.name][-1].id + if len(self.session.db[self.name]) == 0: + return + + # In mentions buffer, items are notification objects which don't have 'pinned' attribute directly. + # But we check the status attached to the notification if it exists. + # However, notifications are strictly chronological usually. Pinned mentions don't exist? + # But let's stick to the safe ID extraction. + # The logic here is tricky because self.session.db stores notification objects, but sometimes just dicts? + # Let's assume they are objects with 'id' attribute. + # Notifications don't have 'pinned', so we just take the min ID. + # But wait, did I change this file previously to use min()? Yes. + # Is there any case where a notification ID is "pinned" (old)? No. + # So min() should be fine here. But for consistency with other buffers if any weird logic exists... + # Actually, let's keep min() as notifications don't support pinning. + + max_id = min(item.id for item in self.session.db[self.name]) try: items = getattr(self.session.api, self.function)(max_id=max_id, limit=self.session.settings["general"]["max_posts_per_call"], types=["mention"], *self.args, **self.kwargs) except Exception as e: diff --git a/srcantiguo/controller/buffers/mastodon/search.py b/srcantiguo/controller/buffers/mastodon/search.py index e583a9ad..04f3a1df 100644 --- a/srcantiguo/controller/buffers/mastodon/search.py +++ b/srcantiguo/controller/buffers/mastodon/search.py @@ -33,10 +33,7 @@ class SearchBuffer(BaseBuffer): self.execution_time = current_time min_id = None if self.name in self.session.db and len(self.session.db[self.name]) > 0: - if self.session.settings["general"]["reverse_timelines"]: - min_id = self.session.db[self.name][0].id - else: - min_id = self.session.db[self.name][-1].id + min_id = max(item.id for item in self.session.db[self.name]) try: results = getattr(self.session.api, self.function)(min_id=min_id, **self.kwargs) except Exception as mess: diff --git a/srcantiguo/controller/mastodon/handler.py b/srcantiguo/controller/mastodon/handler.py index a6e1e301..89e45a04 100644 --- a/srcantiguo/controller/mastodon/handler.py +++ b/srcantiguo/controller/mastodon/handler.py @@ -35,6 +35,7 @@ class Handler(object): compose=_("&Post"), reply=_("Re&ply"), share=_("&Boost"), + quote=_("&Quote"), fav=_("&Add to favorites"), unfav=_("Remove from favorites"), view=_("&Show post"), @@ -92,6 +93,8 @@ class Handler(object): pub.sendMessage("createBuffer", buffer_type="UserBuffer", session_type=session.type, buffer_title=_("Blocked users"), parent_tab=root_position, start=False, kwargs=dict(parent=controller.view.nb, compose_func="compose_user", function="blocks", name="blocked", sessionObject=session, account=name)) elif i == 'notifications': pub.sendMessage("createBuffer", buffer_type="NotificationsBuffer", session_type=session.type, buffer_title=_("Notifications"), parent_tab=root_position, start=False, kwargs=dict(parent=controller.view.nb, compose_func="compose_notification", function="notifications", name="notifications", sessionObject=session, account=name)) + elif i == 'announcements': + pub.sendMessage("createBuffer", buffer_type="AnnouncementsBuffer", session_type=session.type, buffer_title=_("Announcements"), parent_tab=root_position, start=False, kwargs=dict(parent=controller.view.nb, function="announcements", name="announcements", sessionObject=session, account=name, sound="new_event.ogg")) pub.sendMessage("createBuffer", buffer_type="EmptyBuffer", session_type="base", buffer_title=_("Timelines"), parent_tab=root_position, start=False, kwargs=dict(parent=controller.view.nb, name="timelines", account=name)) timelines_position =controller.view.search("timelines", name) for i in session.settings["other_buffers"]["timelines"]: diff --git a/srcantiguo/controller/mastodon/messages.py b/srcantiguo/controller/mastodon/messages.py index 48b2b678..63c6a7da 100644 --- a/srcantiguo/controller/mastodon/messages.py +++ b/srcantiguo/controller/mastodon/messages.py @@ -10,7 +10,7 @@ import languageHandler from twitter_text import parse_tweet, config from mastodon import MastodonError from controller import messages -from sessions.mastodon import templates +from sessions.mastodon import templates, utils from wxUI.dialogs.mastodon import postDialogs from extra.autocompletionUsers import completion from . import userList @@ -282,10 +282,7 @@ class editPost(post): # Extract text from post if item.reblog != None: item = item.reblog - text = item.content - # Remove HTML tags from content - import re - text = re.sub('<[^<]+?>', '', text) + text = utils.html_filter(item.content) # Initialize parent class super(editPost, self).__init__(session, title, caption, text=text, *args, **kwargs) # Store the post ID for editing diff --git a/srcantiguo/controller/mastodon/settings.py b/srcantiguo/controller/mastodon/settings.py index be615baa..db9e6544 100644 --- a/srcantiguo/controller/mastodon/settings.py +++ b/srcantiguo/controller/mastodon/settings.py @@ -52,10 +52,12 @@ class accountSettingsController(globalSettingsController): post_template = self.config["templates"]["post"] conversation_template = self.config["templates"]["conversation"] person_template = self.config["templates"]["person"] - self.dialog.create_templates(post_template=post_template, conversation_template=conversation_template, person_template=person_template) + announcement_template = self.config.get("templates", {}).get("announcement", "$text. Published $published_at. $read") + self.dialog.create_templates(post_template=post_template, conversation_template=conversation_template, person_template=person_template, announcement_template=announcement_template) widgetUtils.connect_event(self.dialog.templates.post, widgetUtils.BUTTON_PRESSED, self.edit_post_template) widgetUtils.connect_event(self.dialog.templates.conversation, widgetUtils.BUTTON_PRESSED, self.edit_conversation_template) widgetUtils.connect_event(self.dialog.templates.person, widgetUtils.BUTTON_PRESSED, self.edit_person_template) + widgetUtils.connect_event(self.dialog.templates.announcement, widgetUtils.BUTTON_PRESSED, self.edit_announcement_template) self.dialog.create_other_buffers() buffer_values = self.get_buffers_list() self.dialog.buffers.insert_buffers(buffer_values) @@ -109,6 +111,15 @@ class accountSettingsController(globalSettingsController): self.config.write() self.dialog.templates.person.SetLabel(_("Edit template for persons. Current template: {}").format(result)) + def edit_announcement_template(self, *args, **kwargs): + template = self.config.get("templates", {}).get("announcement", "$text. Published $published_at. $read") + control = EditTemplate(template=template, type="announcement") + result = control.run_dialog() + if result != "": # Template has been saved. + self.config["templates"]["announcement"] = result + self.config.write() + self.dialog.templates.announcement.SetLabel(_("Edit template for announcements. Current template: {}").format(result)) + def save_configuration(self): if self.config["general"]["relative_times"] != self.dialog.get_value("general", "relative_time"): self.needs_restart = True @@ -204,6 +215,7 @@ class accountSettingsController(globalSettingsController): all_buffers['blocked']=_("Blocked users") all_buffers['muted']=_("Muted users") all_buffers['notifications']=_("Notifications") + all_buffers['announcements']=_("Announcements") list_buffers = [] hidden_buffers=[] all_buffers_keys = list(all_buffers.keys()) diff --git a/srcantiguo/controller/mastodon/templateEditor.py b/srcantiguo/controller/mastodon/templateEditor.py index c4620303..330a304c 100644 --- a/srcantiguo/controller/mastodon/templateEditor.py +++ b/srcantiguo/controller/mastodon/templateEditor.py @@ -2,7 +2,7 @@ import re import wx from typing import List -from sessions.mastodon.templates import post_variables, conversation_variables, person_variables +from sessions.mastodon.templates import post_variables, conversation_variables, person_variables, announcement_variables from wxUI.dialogs import templateDialogs class EditTemplate(object): @@ -13,6 +13,8 @@ class EditTemplate(object): self.variables = post_variables elif type == "conversation": self.variables = conversation_variables + elif type == "announcement": + self.variables = announcement_variables else: self.variables = person_variables self.template: str = template diff --git a/srcantiguo/keymaps/Chicken Nugget.keymap b/srcantiguo/keymaps/Chicken Nugget.keymap index 9d95a47f..29832008 100644 --- a/srcantiguo/keymaps/Chicken Nugget.keymap +++ b/srcantiguo/keymaps/Chicken Nugget.keymap @@ -23,7 +23,6 @@ url = string(default="control+win+b") go_home = string(default="control+win+home") go_end = string(default="control+win+end") delete = string(default="control+win+delete") -edit_post = string(default="") clear_buffer = string(default="control+win+shift+delete") repeat_item = string(default="control+win+space") copy_to_clipboard = string(default="control+win+shift+c") @@ -37,4 +36,13 @@ update_buffer = string(default="control+win+shift+u") ocr_image = string(default="win+alt+o") open_in_browser = string(default="alt+control+win+return") add_alias=string(default="") -vote=string(default="alt+win+shift+v") \ No newline at end of file +vote=string(default="alt+win+shift+v") +edit_post=string(default="") +open_favs_timeline=string(default="") +community_timeline=string(default="") +seekLeft=string(default="") +seekRight=string(default="") +manage_aliases=string(default="") +create_filter=string(default="") +manage_filters=string(default="") +manage_accounts=string(default="") \ No newline at end of file diff --git a/srcantiguo/keymaps/Qwitter.keymap b/srcantiguo/keymaps/Qwitter.keymap index bc0e062f..1a045f50 100644 --- a/srcantiguo/keymaps/Qwitter.keymap +++ b/srcantiguo/keymaps/Qwitter.keymap @@ -33,7 +33,6 @@ go_page_up = string(default="control+win+pageup") go_page_down = string(default="control+win+pagedown") update_profile = string(default="control+win+shift+p") delete = string(default="control+win+delete") -edit_post = string(default="") clear_buffer = string(default="control+win+shift+delete") repeat_item = string(default="control+win+space") copy_to_clipboard = string(default="control+win+shift+c") @@ -56,4 +55,13 @@ update_buffer = string(default="control+win+shift+u") ocr_image = string(default="win+alt+o") open_in_browser = string(default="alt+control+win+return") add_alias=string(default="") -vote=string(default="alt+win+shift+v") \ No newline at end of file +vote=string(default="alt+win+shift+v") +edit_post=string(default="") +open_favs_timeline=string(default="") +community_timeline=string(default="") +seekLeft=string(default="") +seekRight=string(default="") +manage_aliases=string(default="") +create_filter=string(default="") +manage_filters=string(default="") +manage_accounts=string(default="") \ No newline at end of file diff --git a/srcantiguo/keymaps/Windows 10.keymap b/srcantiguo/keymaps/Windows 10.keymap index c12d78ee..3e969518 100644 --- a/srcantiguo/keymaps/Windows 10.keymap +++ b/srcantiguo/keymaps/Windows 10.keymap @@ -33,7 +33,6 @@ go_page_up = string(default="control+win+pageup") go_page_down = string(default="control+win+pagedown") update_profile = string(default="alt+win+p") delete = string(default="alt+win+delete") -edit_post = string(default="") clear_buffer = string(default="alt+win+shift+delete") repeat_item = string(default="alt+win+space") copy_to_clipboard = string(default="alt+win+shift+c") @@ -59,4 +58,13 @@ open_in_browser = string(default="alt+control+win+return") add_alias=string(default="") mute_conversation=string(default="control+alt+win+back") find = string(default="control+win+{") -vote=string(default="alt+win+shift+v") \ No newline at end of file +vote=string(default="alt+win+shift+v") +edit_post=string(default="") +open_favs_timeline=string(default="") +community_timeline=string(default="") +seekLeft=string(default="") +seekRight=string(default="") +manage_aliases=string(default="") +create_filter=string(default="") +manage_filters=string(default="") +manage_accounts=string(default="") \ No newline at end of file diff --git a/srcantiguo/keymaps/Windows11.keymap b/srcantiguo/keymaps/Windows11.keymap index c4301cff..779c3b3b 100644 --- a/srcantiguo/keymaps/Windows11.keymap +++ b/srcantiguo/keymaps/Windows11.keymap @@ -33,7 +33,6 @@ go_page_up = string(default="control+win+pageup") go_page_down = string(default="control+win+pagedown") update_profile = string(default="alt+win+p") delete = string(default="alt+win+delete") -edit_post = string(default="") clear_buffer = string(default="alt+win+shift+delete") repeat_item = string(default="control+alt+win+space") copy_to_clipboard = string(default="alt+win+shift+c") @@ -59,4 +58,13 @@ open_in_browser = string(default="alt+control+win+return") add_alias=string(default="") mute_conversation=string(default="control+alt+win+back") find = string(default="control+win+{") -vote=string(default="alt+win+shift+v") \ No newline at end of file +vote=string(default="alt+win+shift+v") +edit_post=string(default="") +open_favs_timeline=string(default="") +community_timeline=string(default="") +seekLeft=string(default="") +seekRight=string(default="") +manage_aliases=string(default="") +create_filter=string(default="") +manage_filters=string(default="") +manage_accounts=string(default="") \ No newline at end of file diff --git a/srcantiguo/keymaps/base.template b/srcantiguo/keymaps/base.template index dba8fa2c..5f4697bd 100644 --- a/srcantiguo/keymaps/base.template +++ b/srcantiguo/keymaps/base.template @@ -58,4 +58,13 @@ update_buffer = string(default="control+win+shift+u") open_in_browser = string(default="alt+control+win+return") add_alias=string(default="") mute_conversation=string(default="alt+win+shift+delete") -vote=string(default="alt+win+shift+v") \ No newline at end of file +vote=string(default="alt+win+shift+v") +edit_post=string(default="") +open_favs_timeline=string(default="") +community_timeline=string(default="") +seekLeft=string(default="") +seekRight=string(default="") +manage_aliases=string(default="") +create_filter=string(default="") +manage_filters=string(default="") +manage_accounts=string(default="") \ No newline at end of file diff --git a/srcantiguo/keymaps/default.keymap b/srcantiguo/keymaps/default.keymap index 30e6bcbb..fa70c756 100644 --- a/srcantiguo/keymaps/default.keymap +++ b/srcantiguo/keymaps/default.keymap @@ -34,7 +34,6 @@ go_page_up = string(default="control+win+pageup") go_page_down = string(default="control+win+pagedown") update_profile = string(default="alt+win+p") delete = string(default="control+win+delete") -edit_post = string(default="") clear_buffer = string(default="control+win+shift+delete") repeat_item = string(default="control+win+space") copy_to_clipboard = string(default="control+win+shift+c") @@ -60,4 +59,13 @@ ocr_image = string(default="win+alt+o") open_in_browser = string(default="alt+control+win+return") add_alias=string(default="") mute_conversation=string(default="alt+win+shift+delete") -vote=string(default="alt+win+shift+v") \ No newline at end of file +vote=string(default="alt+win+shift+v") +edit_post=string(default="") +open_favs_timeline=string(default="") +community_timeline=string(default="") +seekLeft=string(default="") +seekRight=string(default="") +manage_aliases=string(default="") +create_filter=string(default="") +manage_filters=string(default="") +manage_accounts=string(default="") \ No newline at end of file diff --git a/srcantiguo/keystrokeEditor/actions/mastodon.py b/srcantiguo/keystrokeEditor/actions/mastodon.py index 3f07f152..613687c3 100644 --- a/srcantiguo/keystrokeEditor/actions/mastodon.py +++ b/srcantiguo/keystrokeEditor/actions/mastodon.py @@ -29,7 +29,7 @@ actions = { "go_end": _(u"Jump to the last element of the current buffer"), "go_page_up": _(u"Jump 20 elements up in the current buffer"), "go_page_down": _(u"Jump 20 elements down in the current buffer"), -# "update_profile": _(u"Edit profile"), + "update_profile": _(u"Edit profile"), "delete": _("Delete post"), "clear_buffer": _(u"Empty the current buffer"), "repeat_item": _(u"Repeat last item"), @@ -55,4 +55,14 @@ actions = { "ocr_image": _(u"Extracts the text from a picture and displays the result in a dialog."), "add_alias": _("Adds an alias to an user"), "mute_conversation": _("Mute/Unmute conversation"), + "edit_post": _(u"Edit the selected post"), + "vote": _(u"Vote in the selected poll"), + "open_favs_timeline": _(u"Open favorites timeline"), + "community_timeline": _(u"Open local/federated timeline"), + "seekLeft": _(u"Seek media backward"), + "seekRight": _(u"Seek media forward"), + "manage_aliases": _(u"Manage user aliases"), + "create_filter": _(u"Create a new filter"), + "manage_filters": _(u"Manage filters"), + "manage_accounts": _(u"Manage accounts"), } \ No newline at end of file diff --git a/srcantiguo/mastodon.defaults b/srcantiguo/mastodon.defaults index 525bfc84..b6b06af3 100644 --- a/srcantiguo/mastodon.defaults +++ b/srcantiguo/mastodon.defaults @@ -14,7 +14,7 @@ persist_size = integer(default=0) load_cache_in_memory=boolean(default=True) show_screen_names = boolean(default=False) hide_emojis = boolean(default=False) -buffer_order = list(default=list('home', 'local', 'mentions', 'direct_messages', 'sent', 'favorites', 'bookmarks', 'followers', 'following', 'blocked', 'muted', 'notifications')) +buffer_order = list(default=list('home', 'local', 'mentions', 'direct_messages', 'sent', 'favorites', 'bookmarks', 'followers', 'following', 'blocked', 'muted', 'notifications', 'announcements')) boost_mode = string(default="ask") disable_streaming = boolean(default=False) @@ -54,6 +54,7 @@ post = string(default="$display_name, $safe_text $image_descriptions $date. $vis person = string(default="$display_name (@$screen_name). $followers followers, $following following, $posts posts. Joined $created_at.") conversation = string(default="Conversation with $users. Last message: $last_post") notification = string(default="$display_name $text, $date") +announcement = string(default="$text. Published $published_at. $read") [filters] diff --git a/srcantiguo/sessions/mastodon/compose.py b/srcantiguo/sessions/mastodon/compose.py index b9d9534f..15c3aebf 100644 --- a/srcantiguo/sessions/mastodon/compose.py +++ b/srcantiguo/sessions/mastodon/compose.py @@ -84,4 +84,10 @@ def compose_notification(notification, db, settings, relative_times, show_screen filtered = utils.evaluate_filters(post=notification, current_context="notifications") if filtered != None: text = _("hidden by filter {}").format(filtered) - return [user, text, ts] \ No newline at end of file + return [user, text, ts] + +def compose_announcement(announcement, db, settings, relative_times, show_screen_names, safe=False): + # Use the default template or a configured one if available + template = settings.get("templates", {}).get("announcement", templates.announcement_default_template) + text = templates.render_announcement(announcement, template, settings, relative_times, db["utc_offset"]) + return [text] \ No newline at end of file diff --git a/srcantiguo/sessions/mastodon/session.py b/srcantiguo/sessions/mastodon/session.py index 658fd3c4..8a205862 100644 --- a/srcantiguo/sessions/mastodon/session.py +++ b/srcantiguo/sessions/mastodon/session.py @@ -217,38 +217,69 @@ class Session(base.baseSession): self.sound.play(_sound) return val - def send_post(self, reply_to=None, visibility=None, language=None, posts=[]): + def _send_quote_post(self, text, quote_id, visibility, sensitive, spoiler_text, language, scheduled_at, in_reply_to_id=None, media_ids=[], poll=None): + """Internal helper to send a quote post using direct API call.""" + params = { + 'status': text, + 'visibility': visibility, + 'quoted_status_id': quote_id, + } + if in_reply_to_id: + params['in_reply_to_id'] = in_reply_to_id + if sensitive: + params['sensitive'] = sensitive + if spoiler_text: + params['spoiler_text'] = spoiler_text + if language: + params['language'] = language + if scheduled_at: + if hasattr(scheduled_at, 'isoformat'): + params['scheduled_at'] = scheduled_at.isoformat() + else: + params['scheduled_at'] = scheduled_at + if media_ids: + params['media_ids'] = media_ids + if poll: + params['poll'] = poll + + # Use the internal API request method directly + return self.api._Mastodon__api_request('POST', '/api/v1/statuses', params) + + def send_post(self, reply_to=None, quote_id=None, visibility=None, language=None, posts=[]): """ Convenience function to send a thread. """ in_reply_to_id = reply_to for obj in posts: text = obj.get("text") scheduled_at = obj.get("scheduled_at") - if len(obj["attachments"]) == 0: + + # Prepare media and polls first as they are needed for both standard and quote posts + media_ids = [] + poll = None + if len(obj["attachments"]) > 0: try: - item = self.api_call(call_name="status_post", status=text, _sound="tweet_send.ogg", in_reply_to_id=in_reply_to_id, visibility=visibility, sensitive=obj["sensitive"], spoiler_text=obj["spoiler_text"], language=language, scheduled_at=scheduled_at) - # If it fails, let's basically send an event with all passed info so we will catch it later. - except Exception as e: - pub.sendMessage("mastodon.error_post", name=self.get_name(), reply_to=reply_to, visibility=visibility, posts=posts, lang=language) - return - if item != None: - in_reply_to_id = item["id"] - else: - media_ids = [] - try: - poll = None if len(obj["attachments"]) == 1 and obj["attachments"][0]["type"] == "poll": poll = self.api.make_poll(options=obj["attachments"][0]["options"], expires_in=obj["attachments"][0]["expires_in"], multiple=obj["attachments"][0]["multiple"], hide_totals=obj["attachments"][0]["hide_totals"]) else: for i in obj["attachments"]: media = self.api_call("media_post", media_file=i["file"], description=i["description"], synchronous=True) media_ids.append(media.id) - item = self.api_call(call_name="status_post", status=text, _sound="tweet_send.ogg", in_reply_to_id=in_reply_to_id, media_ids=media_ids, visibility=visibility, poll=poll, sensitive=obj["sensitive"], spoiler_text=obj["spoiler_text"], language=language, scheduled_at=scheduled_at) - if item != None: - in_reply_to_id = item["id"] except Exception as e: - pub.sendMessage("mastodon.error_post", name=self.get_name(), reply_to=reply_to, visibility=visibility, posts=posts, lang=language) + pub.sendMessage("mastodon.error_post", name=self.get_name(), reply_to=reply_to, visibility=visibility, posts=posts, language=language) return + try: + if quote_id: + item = self._send_quote_post(text, quote_id, visibility, obj["sensitive"], obj["spoiler_text"], language, scheduled_at, in_reply_to_id, media_ids, poll) + self.sound.play("tweet_send.ogg") + else: + item = self.api_call(call_name="status_post", status=text, _sound="tweet_send.ogg", in_reply_to_id=in_reply_to_id, media_ids=media_ids, visibility=visibility, poll=poll, sensitive=obj["sensitive"], spoiler_text=obj["spoiler_text"], language=language, scheduled_at=scheduled_at) + + if item != None: + in_reply_to_id = item["id"] + except Exception as e: + pub.sendMessage("mastodon.error_post", name=self.get_name(), reply_to=reply_to, visibility=visibility, posts=posts, language=language) + return + def edit_post(self, post_id, posts=[]): """ Convenience function to edit a post. Only the first item in posts list is used as threads cannot be edited. diff --git a/srcantiguo/sessions/mastodon/templates.py b/srcantiguo/sessions/mastodon/templates.py index 2674bea3..99de0dde 100644 --- a/srcantiguo/sessions/mastodon/templates.py +++ b/srcantiguo/sessions/mastodon/templates.py @@ -13,12 +13,14 @@ post_variables = ["date", "display_name", "screen_name", "source", "lang", "safe person_variables = ["display_name", "screen_name", "description", "followers", "following", "favorites", "posts", "created_at"] conversation_variables = ["users", "last_post"] notification_variables = ["display_name", "screen_name", "text", "date"] +announcement_variables = ["text", "published_at", "updated_at", "starts_at", "ends_at", "read"] # Default, translatable templates. post_default_template = _("$display_name, $text $image_descriptions $date. $source") dm_sent_default_template = _("Dm to $recipient_display_name, $text $date") person_default_template = _("$display_name (@$screen_name). $followers followers, $following following, $posts posts. Joined $created_at.") notification_default_template = _("$display_name $text, $date") +announcement_default_template = _("$text. Published $published_at. $read") def process_date(field, relative_times=True, offset_hours=0): original_date = arrow.get(field) @@ -185,3 +187,23 @@ def render_notification(notification, template, post_template, settings, relativ result = Template(_(template)).safe_substitute(**available_data) result = result.replace(" . ", "") return result + +def render_announcement(announcement, template, settings, relative_times=False, offset_hours=0): + """ Renders any given announcement according to the passed template. """ + global announcement_variables + available_data = dict() + # Process dates + for date_field in ["published_at", "updated_at", "starts_at", "ends_at"]: + if hasattr(announcement, date_field) and getattr(announcement, date_field) is not None: + available_data[date_field] = process_date(getattr(announcement, date_field), relative_times, offset_hours) + else: + available_data[date_field] = "" + + available_data["text"] = utils.html_filter(announcement.content) + if announcement.read: + available_data["read"] = _("Read") + else: + available_data["read"] = _("Unread") + + result = Template(_(template)).safe_substitute(**available_data) + return result diff --git a/srcantiguo/setup.py b/srcantiguo/setup.py index b1a0069f..34b27cb4 100644 --- a/srcantiguo/setup.py +++ b/srcantiguo/setup.py @@ -3,7 +3,7 @@ import sys import application import platform import os -from cx_Freeze import setup, Executable, winmsvcr +from cx_Freeze import setup, Executable from requests import certs def get_architecture_files(): @@ -34,7 +34,7 @@ def find_accessible_output2_datafiles(): base = None if sys.platform == 'win32': - base = 'Win32GUI' + base = 'GUI' build_exe_options = dict( build_exe="dist", @@ -51,8 +51,6 @@ executables = [ Executable('main.py', base=base, target_name="twblue") ] -winmsvcr.FILES = () -winmsvcr.FILES_TO_DUPLICATE = () setup(name=application.name, version=application.version, description=application.description, diff --git a/srcantiguo/wxUI/buffers/mastodon/__init__.py b/srcantiguo/wxUI/buffers/mastodon/__init__.py index 32791133..aa0305da 100644 --- a/srcantiguo/wxUI/buffers/mastodon/__init__.py +++ b/srcantiguo/wxUI/buffers/mastodon/__init__.py @@ -2,4 +2,5 @@ from .base import basePanel from .conversationList import conversationListPanel from .notifications import notificationsPanel -from .user import userPanel \ No newline at end of file +from .user import userPanel +from .announcements import announcementsPanel diff --git a/srcantiguo/wxUI/buffers/mastodon/announcements.py b/srcantiguo/wxUI/buffers/mastodon/announcements.py new file mode 100644 index 00000000..d6639925 --- /dev/null +++ b/srcantiguo/wxUI/buffers/mastodon/announcements.py @@ -0,0 +1,36 @@ +# -*- coding: utf-8 -*- +import wx +from multiplatform_widgets import widgets + +class announcementsPanel(wx.Panel): + + def set_focus_function(self, f): + self.list.list.Bind(wx.EVT_LIST_ITEM_FOCUSED, f) + + def create_list(self): + self.list = widgets.list(self, _("Announcement"), style=wx.LC_REPORT|wx.LC_SINGLE_SEL|wx.LC_VRULES) + self.list.set_windows_size(0, 800) + self.list.set_size() + + def __init__(self, parent, name): + super(announcementsPanel, self).__init__(parent) + self.name = name + self.type = "baseBuffer" + self.sizer = wx.BoxSizer(wx.VERTICAL) + self.create_list() + self.dismiss = wx.Button(self, -1, _("Dismiss")) + btnSizer = wx.BoxSizer(wx.HORIZONTAL) + btnSizer.Add(self.dismiss, 0, wx.ALL, 5) + self.sizer.Add(btnSizer, 0, wx.ALL, 5) + self.sizer.Add(self.list.list, 1, wx.ALL|wx.EXPAND, 5) + self.SetSizer(self.sizer) + self.SetClientSize(self.sizer.CalcMin()) + + def set_position(self, reversed=False): + if reversed == False: + self.list.select_item(self.list.get_count()-1) + else: + self.list.select_item(0) + + def set_focus_in_list(self): + self.list.list.SetFocus() diff --git a/srcantiguo/wxUI/dialogs/mastodon/configuration.py b/srcantiguo/wxUI/dialogs/mastodon/configuration.py index 9138ea63..4fada629 100644 --- a/srcantiguo/wxUI/dialogs/mastodon/configuration.py +++ b/srcantiguo/wxUI/dialogs/mastodon/configuration.py @@ -47,7 +47,7 @@ class generalAccount(wx.Panel, baseDialog.BaseWXDialog): self.SetSizer(sizer) class templates(wx.Panel, baseDialog.BaseWXDialog): - def __init__(self, parent, post_template, conversation_template, person_template): + def __init__(self, parent, post_template, conversation_template, person_template, announcement_template): super(templates, self).__init__(parent) sizer = wx.BoxSizer(wx.VERTICAL) self.post = wx.Button(self, wx.ID_ANY, _("Edit template for &posts. Current template: {}").format(post_template)) @@ -56,6 +56,8 @@ class templates(wx.Panel, baseDialog.BaseWXDialog): sizer.Add(self.conversation, 0, wx.ALL, 5) self.person = wx.Button(self, wx.ID_ANY, _("Edit template for p&ersons. Current template: {}").format(person_template)) sizer.Add(self.person, 0, wx.ALL, 5) + self.announcement = wx.Button(self, wx.ID_ANY, _("Edit template for &announcements. Current template: {}").format(announcement_template)) + sizer.Add(self.announcement, 0, wx.ALL, 5) self.SetSizer(sizer) class sound(wx.Panel): @@ -152,8 +154,8 @@ class configurationDialog(baseDialog.BaseWXDialog): self.buffers = other_buffers(self.notebook) self.notebook.AddPage(self.buffers, _(u"Buffers")) - def create_templates(self, post_template, conversation_template, person_template): - self.templates = templates(self.notebook, post_template=post_template, conversation_template=conversation_template, person_template=person_template) + def create_templates(self, post_template, conversation_template, person_template, announcement_template): + self.templates = templates(self.notebook, post_template=post_template, conversation_template=conversation_template, person_template=person_template, announcement_template=announcement_template) self.notebook.AddPage(self.templates, _("Templates")) def create_sound(self, output_devices, input_devices, soundpacks): diff --git a/srcantiguo/wxUI/dialogs/mastodon/dialogs.py b/srcantiguo/wxUI/dialogs/mastodon/dialogs.py index 1f945d82..865dd36a 100644 --- a/srcantiguo/wxUI/dialogs/mastodon/dialogs.py +++ b/srcantiguo/wxUI/dialogs/mastodon/dialogs.py @@ -2,11 +2,43 @@ import wx import application +class BoostDialog(wx.Dialog): + def __init__(self): + super(BoostDialog, self).__init__(None, title=_("Boost")) + p = wx.Panel(self) + sizer = wx.BoxSizer(wx.VERTICAL) + lbl = wx.StaticText(p, wx.ID_ANY, _("What would you like to do with this post?")) + sizer.Add(lbl, 0, wx.ALL, 10) + + btn_sizer = wx.BoxSizer(wx.HORIZONTAL) + self.btn_boost = wx.Button(p, wx.ID_ANY, _("Boost")) + self.btn_quote = wx.Button(p, wx.ID_ANY, _("Quote")) + self.btn_cancel = wx.Button(p, wx.ID_CANCEL, _("Cancel")) + + btn_sizer.Add(self.btn_boost, 0, wx.ALL, 5) + btn_sizer.Add(self.btn_quote, 0, wx.ALL, 5) + btn_sizer.Add(self.btn_cancel, 0, wx.ALL, 5) + + sizer.Add(btn_sizer, 0, wx.ALIGN_CENTER) + p.SetSizer(sizer) + sizer.Fit(self) + + self.btn_boost.Bind(wx.EVT_BUTTON, self.on_boost) + self.btn_quote.Bind(wx.EVT_BUTTON, self.on_quote) + self.result = 0 + + def on_boost(self, event): + self.result = 1 + self.EndModal(wx.ID_OK) + + def on_quote(self, event): + self.result = 2 + self.EndModal(wx.ID_OK) + def boost_question(): - result = False - dlg = wx.MessageDialog(None, _("Would you like to share this post?"), _("Boost"), wx.YES_NO|wx.ICON_QUESTION) - if dlg.ShowModal() == wx.ID_YES: - result = True + dlg = BoostDialog() + dlg.ShowModal() + result = dlg.result dlg.Destroy() return result diff --git a/srcantiguo/wxUI/dialogs/mastodon/menus.py b/srcantiguo/wxUI/dialogs/mastodon/menus.py index 62dd3407..b7277ab4 100644 --- a/srcantiguo/wxUI/dialogs/mastodon/menus.py +++ b/srcantiguo/wxUI/dialogs/mastodon/menus.py @@ -6,6 +6,8 @@ class base(wx.Menu): super(base, self).__init__() self.boost = wx.MenuItem(self, wx.ID_ANY, _("&Boost")) self.Append(self.boost) + self.quote = wx.MenuItem(self, wx.ID_ANY, _("&Quote")) + self.Append(self.quote) self.reply = wx.MenuItem(self, wx.ID_ANY, _(u"Re&ply")) self.Append(self.reply) self.edit = wx.MenuItem(self, wx.ID_ANY, _(u"&Edit")) @@ -38,6 +40,8 @@ class notification(wx.Menu): if item in valid_types: self.boost = wx.MenuItem(self, wx.ID_ANY, _("&Boost")) self.Append(self.boost) + self.quote = wx.MenuItem(self, wx.ID_ANY, _("&Quote")) + self.Append(self.quote) self.reply = wx.MenuItem(self, wx.ID_ANY, _(u"Re&ply")) self.Append(self.reply) self.edit = wx.MenuItem(self, wx.ID_ANY, _(u"&Edit"))