Implement post editing functionality for Mastodon

Add ability to edit posts in Mastodon with full support for:
- Editing post text and content warnings
- Re-uploading or keeping existing media attachments
- Editing poll options (for posts with polls)
- Modifying visibility and language settings
- All features available through web interface

Changes:
- Add edit_post() method in Mastodon session to handle API calls
- Create editPost dialog class that loads existing post data
- Add edit_status() method to buffer controllers
- Add Edit menu item to base and notification menus
- Register edit_post action in all keymaps (no default key assigned)
- Add edit_post() action handler in main controller

The edit option is only enabled for the user's own posts (not boosts).
Users can access the feature through the context menu or by assigning
a keyboard shortcut in the keymap editor.
This commit is contained in:
Claude
2025-11-06 14:37:12 +00:00
parent cbafb7da69
commit 977de1332a
11 changed files with 121 additions and 0 deletions

View File

@@ -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

View File

@@ -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)

View File

@@ -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)

View File

@@ -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