diff --git a/src/controller/buffers/mastodon/base.py b/src/controller/buffers/mastodon/base.py index 0783d9cb..fb08902d 100644 --- a/src/controller/buffers/mastodon/base.py +++ b/src/controller/buffers/mastodon/base.py @@ -280,6 +280,12 @@ class BaseBuffer(base.Buffer): return menu = menus.base() widgetUtils.connect_event(menu, widgetUtils.MENU, self.reply, menuitem=menu.reply) + # Enable/disable edit based on whether the post belongs to the user + item = self.get_item() + if item and item.account.id == self.session.db["user_id"] and item.reblog == None: + widgetUtils.connect_event(menu, widgetUtils.MENU, self.edit_status, menuitem=menu.edit) + else: + menu.edit.Enable(False) 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) @@ -501,6 +507,25 @@ class BaseBuffer(base.Buffer): log.exception("") self.session.db[self.name] = items + def edit_status(self, event=None, item=None, *args, **kwargs): + if item == None: + item = self.get_item() + # Check if the post belongs to the current user + if item.account.id != self.session.db["user_id"] or item.reblog != None: + output.speak(_("You can only edit your own posts.")) + return + # Create edit dialog with existing post data + title = _("Edit post") + caption = _("Edit your post here") + post = messages.editPost(session=self.session, item=item, title=title, caption=caption) + response = post.message.ShowModal() + if response == wx.ID_OK: + post_data = post.get_data() + # Call edit_post method in session + call_threaded(self.session.edit_post, post_id=post.post_id, posts=post_data, visibility=post.get_visibility(), language=post.get_language()) + if hasattr(post.message, "destroy"): + post.message.destroy() + def user_details(self): item = self.get_item() pass diff --git a/src/controller/buffers/mastodon/notifications.py b/src/controller/buffers/mastodon/notifications.py index 5c142950..cce39f4d 100644 --- a/src/controller/buffers/mastodon/notifications.py +++ b/src/controller/buffers/mastodon/notifications.py @@ -161,6 +161,13 @@ class NotificationsBuffer(BaseBuffer): menu = menus.notification(notification.type) if self.is_post(): widgetUtils.connect_event(menu, widgetUtils.MENU, self.reply, menuitem=menu.reply) + # Enable/disable edit based on whether the post belongs to the user + if hasattr(menu, 'edit'): + status = self.get_post() + if status and status.account.id == self.session.db["user_id"] and status.reblog == None: + widgetUtils.connect_event(menu, widgetUtils.MENU, self.edit_status, menuitem=menu.edit) + else: + menu.edit.Enable(False) 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) diff --git a/src/controller/mainController.py b/src/controller/mainController.py index a4264cf6..fef23b5c 100644 --- a/src/controller/mainController.py +++ b/src/controller/mainController.py @@ -449,6 +449,15 @@ class Controller(object): buffer = self.search_buffer(buffer.name, buffer.account) buffer.destroy_status() + def edit_post(self, *args, **kwargs): + """ Edits a post in the current buffer. + Users can only edit their own posts.""" + buffer = self.view.get_current_buffer() + if hasattr(buffer, "account"): + buffer = self.search_buffer(buffer.name, buffer.account) + if hasattr(buffer, "edit_status"): + buffer.edit_status() + def exit(self, *args, **kwargs): if config.app["app-settings"]["ask_at_exit"] == True: answer = commonMessageDialogs.exit_dialog(self.view) diff --git a/src/controller/mastodon/messages.py b/src/controller/mastodon/messages.py index 9e61ab70..7502d857 100644 --- a/src/controller/mastodon/messages.py +++ b/src/controller/mastodon/messages.py @@ -262,6 +262,47 @@ class post(messages.basicMessage): visibility_setting = visibility_settings.index(setting) self.message.visibility.SetSelection(setting) +class editPost(post): + def __init__(self, session, item, title, caption, *args, **kwargs): + """ Initialize edit dialog with existing post data. """ + # 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) + # Initialize parent class + super(editPost, self).__init__(session, title, caption, text=text, *args, **kwargs) + # Store the post ID for editing + self.post_id = item.id + # Set visibility + visibility_settings = dict(public=0, unlisted=1, private=2, direct=3) + self.message.visibility.SetSelection(visibility_settings.get(item.visibility, 0)) + # Set language + if item.language: + self.set_language(item.language) + # Set sensitive content and spoiler + if item.sensitive: + self.message.sensitive.SetValue(True) + if item.spoiler_text: + self.message.spoiler.ChangeValue(item.spoiler_text) + self.message.on_sensitivity_changed() + # Load existing media attachments + if hasattr(item, 'media_attachments') and len(item.media_attachments) > 0: + for media in item.media_attachments: + media_info = { + "id": media.id, # Keep the existing media ID + "type": media.type, + "file": media.url, # URL of existing media + "description": media.description or "" + } + self.attachments.append(media_info) + # Display in the attachment list + self.message.add_item(item=[media.url.split('/')[-1], media.type, media.description or ""]) + # Update text processor to reflect the loaded content + self.text_processor() + class viewPost(post): def __init__(self, session, post, offset_hours=0, date="", item_url=""): self.session = session diff --git a/src/keymaps/Chicken Nugget.keymap b/src/keymaps/Chicken Nugget.keymap index aad998a9..9d95a47f 100644 --- a/src/keymaps/Chicken Nugget.keymap +++ b/src/keymaps/Chicken Nugget.keymap @@ -23,6 +23,7 @@ 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") diff --git a/src/keymaps/Qwitter.keymap b/src/keymaps/Qwitter.keymap index 3a73baea..7a8bc30a 100644 --- a/src/keymaps/Qwitter.keymap +++ b/src/keymaps/Qwitter.keymap @@ -33,6 +33,7 @@ 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") diff --git a/src/keymaps/Windows 10.keymap b/src/keymaps/Windows 10.keymap index 3e21c51b..426c598f 100644 --- a/src/keymaps/Windows 10.keymap +++ b/src/keymaps/Windows 10.keymap @@ -33,6 +33,7 @@ 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") diff --git a/src/keymaps/Windows11.keymap b/src/keymaps/Windows11.keymap index 518118f8..9ed24488 100644 --- a/src/keymaps/Windows11.keymap +++ b/src/keymaps/Windows11.keymap @@ -33,6 +33,7 @@ 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") diff --git a/src/keymaps/default.keymap b/src/keymaps/default.keymap index 19d9ebc0..8e4bf9b2 100644 --- a/src/keymaps/default.keymap +++ b/src/keymaps/default.keymap @@ -34,6 +34,7 @@ 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") diff --git a/src/sessions/mastodon/session.py b/src/sessions/mastodon/session.py index 57e153ed..de4baad9 100644 --- a/src/sessions/mastodon/session.py +++ b/src/sessions/mastodon/session.py @@ -248,6 +248,36 @@ class Session(base.baseSession): pub.sendMessage("mastodon.error_post", name=self.get_name(), reply_to=reply_to, visibility=visibility, posts=posts, lang=language) return + def edit_post(self, post_id, visibility=None, language=None, posts=[]): + """ Convenience function to edit a post. Only the first item in posts list is used as threads cannot be edited. """ + if len(posts) == 0: + return + obj = posts[0] + text = obj.get("text") + media_ids = [] + try: + poll = None + # Handle poll attachments + 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"]) + # Handle media attachments + elif len(obj["attachments"]) > 0: + for i in obj["attachments"]: + # If attachment has an 'id', it's an existing media that we keep + if "id" in i: + media_ids.append(i["id"]) + # Otherwise it's a new file to upload + elif "file" in i: + media = self.api_call("media_post", media_file=i["file"], description=i["description"], synchronous=True) + media_ids.append(media.id) + # Call status_update API + item = self.api_call(call_name="status_update", id=post_id, status=text, _sound="tweet_send.ogg", media_ids=media_ids if len(media_ids) > 0 else None, visibility=visibility, poll=poll, sensitive=obj["sensitive"], spoiler_text=obj["spoiler_text"], language=language) + return item + except Exception as e: + log.exception("Error updating post: {}".format(str(e))) + output.speak(_("Error editing post: {}").format(str(e))) + return None + def get_name(self): instance = self.settings["mastodon"]["instance"] instance = instance.replace("https://", "") diff --git a/src/wxUI/dialogs/mastodon/menus.py b/src/wxUI/dialogs/mastodon/menus.py index 63a9cb56..7947d942 100644 --- a/src/wxUI/dialogs/mastodon/menus.py +++ b/src/wxUI/dialogs/mastodon/menus.py @@ -8,6 +8,8 @@ class base(wx.Menu): self.Append(self.boost) 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")) + self.Append(self.edit) self.fav = wx.MenuItem(self, wx.ID_ANY, _(u"&Add to favorites")) self.Append(self.fav) self.unfav = wx.MenuItem(self, wx.ID_ANY, _(u"R&emove from favorites")) @@ -36,6 +38,8 @@ class notification(wx.Menu): self.Append(self.boost) 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")) + self.Append(self.edit) self.fav = wx.MenuItem(self, wx.ID_ANY, _(u"&Add to favorites")) self.Append(self.fav) self.unfav = wx.MenuItem(self, wx.ID_ANY, _(u"R&emove from favorites"))