mirror of
https://github.com/MCV-Software/TWBlue.git
synced 2025-11-06 22:47:04 +00:00
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:
@@ -280,6 +280,12 @@ class BaseBuffer(base.Buffer):
|
|||||||
return
|
return
|
||||||
menu = menus.base()
|
menu = menus.base()
|
||||||
widgetUtils.connect_event(menu, widgetUtils.MENU, self.reply, menuitem=menu.reply)
|
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)
|
widgetUtils.connect_event(menu, widgetUtils.MENU, self.user_actions, menuitem=menu.userActions)
|
||||||
if self.can_share() == True:
|
if self.can_share() == True:
|
||||||
widgetUtils.connect_event(menu, widgetUtils.MENU, self.share_item, menuitem=menu.boost)
|
widgetUtils.connect_event(menu, widgetUtils.MENU, self.share_item, menuitem=menu.boost)
|
||||||
@@ -501,6 +507,25 @@ class BaseBuffer(base.Buffer):
|
|||||||
log.exception("")
|
log.exception("")
|
||||||
self.session.db[self.name] = items
|
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):
|
def user_details(self):
|
||||||
item = self.get_item()
|
item = self.get_item()
|
||||||
pass
|
pass
|
||||||
|
|||||||
@@ -161,6 +161,13 @@ class NotificationsBuffer(BaseBuffer):
|
|||||||
menu = menus.notification(notification.type)
|
menu = menus.notification(notification.type)
|
||||||
if self.is_post():
|
if self.is_post():
|
||||||
widgetUtils.connect_event(menu, widgetUtils.MENU, self.reply, menuitem=menu.reply)
|
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)
|
widgetUtils.connect_event(menu, widgetUtils.MENU, self.user_actions, menuitem=menu.userActions)
|
||||||
if self.can_share() == True:
|
if self.can_share() == True:
|
||||||
widgetUtils.connect_event(menu, widgetUtils.MENU, self.share_item, menuitem=menu.boost)
|
widgetUtils.connect_event(menu, widgetUtils.MENU, self.share_item, menuitem=menu.boost)
|
||||||
|
|||||||
@@ -449,6 +449,15 @@ class Controller(object):
|
|||||||
buffer = self.search_buffer(buffer.name, buffer.account)
|
buffer = self.search_buffer(buffer.name, buffer.account)
|
||||||
buffer.destroy_status()
|
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):
|
def exit(self, *args, **kwargs):
|
||||||
if config.app["app-settings"]["ask_at_exit"] == True:
|
if config.app["app-settings"]["ask_at_exit"] == True:
|
||||||
answer = commonMessageDialogs.exit_dialog(self.view)
|
answer = commonMessageDialogs.exit_dialog(self.view)
|
||||||
|
|||||||
@@ -262,6 +262,47 @@ class post(messages.basicMessage):
|
|||||||
visibility_setting = visibility_settings.index(setting)
|
visibility_setting = visibility_settings.index(setting)
|
||||||
self.message.visibility.SetSelection(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):
|
class viewPost(post):
|
||||||
def __init__(self, session, post, offset_hours=0, date="", item_url=""):
|
def __init__(self, session, post, offset_hours=0, date="", item_url=""):
|
||||||
self.session = session
|
self.session = session
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ url = string(default="control+win+b")
|
|||||||
go_home = string(default="control+win+home")
|
go_home = string(default="control+win+home")
|
||||||
go_end = string(default="control+win+end")
|
go_end = string(default="control+win+end")
|
||||||
delete = string(default="control+win+delete")
|
delete = string(default="control+win+delete")
|
||||||
|
edit_post = string(default="")
|
||||||
clear_buffer = string(default="control+win+shift+delete")
|
clear_buffer = string(default="control+win+shift+delete")
|
||||||
repeat_item = string(default="control+win+space")
|
repeat_item = string(default="control+win+space")
|
||||||
copy_to_clipboard = string(default="control+win+shift+c")
|
copy_to_clipboard = string(default="control+win+shift+c")
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ go_page_up = string(default="control+win+pageup")
|
|||||||
go_page_down = string(default="control+win+pagedown")
|
go_page_down = string(default="control+win+pagedown")
|
||||||
update_profile = string(default="control+win+shift+p")
|
update_profile = string(default="control+win+shift+p")
|
||||||
delete = string(default="control+win+delete")
|
delete = string(default="control+win+delete")
|
||||||
|
edit_post = string(default="")
|
||||||
clear_buffer = string(default="control+win+shift+delete")
|
clear_buffer = string(default="control+win+shift+delete")
|
||||||
repeat_item = string(default="control+win+space")
|
repeat_item = string(default="control+win+space")
|
||||||
copy_to_clipboard = string(default="control+win+shift+c")
|
copy_to_clipboard = string(default="control+win+shift+c")
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ go_page_up = string(default="control+win+pageup")
|
|||||||
go_page_down = string(default="control+win+pagedown")
|
go_page_down = string(default="control+win+pagedown")
|
||||||
update_profile = string(default="alt+win+p")
|
update_profile = string(default="alt+win+p")
|
||||||
delete = string(default="alt+win+delete")
|
delete = string(default="alt+win+delete")
|
||||||
|
edit_post = string(default="")
|
||||||
clear_buffer = string(default="alt+win+shift+delete")
|
clear_buffer = string(default="alt+win+shift+delete")
|
||||||
repeat_item = string(default="alt+win+space")
|
repeat_item = string(default="alt+win+space")
|
||||||
copy_to_clipboard = string(default="alt+win+shift+c")
|
copy_to_clipboard = string(default="alt+win+shift+c")
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ go_page_up = string(default="control+win+pageup")
|
|||||||
go_page_down = string(default="control+win+pagedown")
|
go_page_down = string(default="control+win+pagedown")
|
||||||
update_profile = string(default="alt+win+p")
|
update_profile = string(default="alt+win+p")
|
||||||
delete = string(default="alt+win+delete")
|
delete = string(default="alt+win+delete")
|
||||||
|
edit_post = string(default="")
|
||||||
clear_buffer = string(default="alt+win+shift+delete")
|
clear_buffer = string(default="alt+win+shift+delete")
|
||||||
repeat_item = string(default="control+alt+win+space")
|
repeat_item = string(default="control+alt+win+space")
|
||||||
copy_to_clipboard = string(default="alt+win+shift+c")
|
copy_to_clipboard = string(default="alt+win+shift+c")
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ go_page_up = string(default="control+win+pageup")
|
|||||||
go_page_down = string(default="control+win+pagedown")
|
go_page_down = string(default="control+win+pagedown")
|
||||||
update_profile = string(default="alt+win+p")
|
update_profile = string(default="alt+win+p")
|
||||||
delete = string(default="control+win+delete")
|
delete = string(default="control+win+delete")
|
||||||
|
edit_post = string(default="")
|
||||||
clear_buffer = string(default="control+win+shift+delete")
|
clear_buffer = string(default="control+win+shift+delete")
|
||||||
repeat_item = string(default="control+win+space")
|
repeat_item = string(default="control+win+space")
|
||||||
copy_to_clipboard = string(default="control+win+shift+c")
|
copy_to_clipboard = string(default="control+win+shift+c")
|
||||||
|
|||||||
@@ -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)
|
pub.sendMessage("mastodon.error_post", name=self.get_name(), reply_to=reply_to, visibility=visibility, posts=posts, lang=language)
|
||||||
return
|
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):
|
def get_name(self):
|
||||||
instance = self.settings["mastodon"]["instance"]
|
instance = self.settings["mastodon"]["instance"]
|
||||||
instance = instance.replace("https://", "")
|
instance = instance.replace("https://", "")
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ class base(wx.Menu):
|
|||||||
self.Append(self.boost)
|
self.Append(self.boost)
|
||||||
self.reply = wx.MenuItem(self, wx.ID_ANY, _(u"Re&ply"))
|
self.reply = wx.MenuItem(self, wx.ID_ANY, _(u"Re&ply"))
|
||||||
self.Append(self.reply)
|
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.fav = wx.MenuItem(self, wx.ID_ANY, _(u"&Add to favorites"))
|
||||||
self.Append(self.fav)
|
self.Append(self.fav)
|
||||||
self.unfav = wx.MenuItem(self, wx.ID_ANY, _(u"R&emove from favorites"))
|
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.Append(self.boost)
|
||||||
self.reply = wx.MenuItem(self, wx.ID_ANY, _(u"Re&ply"))
|
self.reply = wx.MenuItem(self, wx.ID_ANY, _(u"Re&ply"))
|
||||||
self.Append(self.reply)
|
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.fav = wx.MenuItem(self, wx.ID_ANY, _(u"&Add to favorites"))
|
||||||
self.Append(self.fav)
|
self.Append(self.fav)
|
||||||
self.unfav = wx.MenuItem(self, wx.ID_ANY, _(u"R&emove from favorites"))
|
self.unfav = wx.MenuItem(self, wx.ID_ANY, _(u"R&emove from favorites"))
|
||||||
|
|||||||
Reference in New Issue
Block a user