fix: avoid passing params that are not editable to API Calls; display polls as attachments in post edit dialog, warns user about vote resetting when editing a post with poll

This commit is contained in:
2025-11-06 17:39:55 -06:00
parent de837e15b9
commit 3af372973d
2 changed files with 104 additions and 16 deletions

View File

@@ -514,6 +514,29 @@ class BaseBuffer(base.Buffer):
if item.account.id != self.session.db["user_id"] or item.reblog != None: if item.account.id != self.session.db["user_id"] or item.reblog != None:
output.speak(_("You can only edit your own posts.")) output.speak(_("You can only edit your own posts."))
return return
# Check if post has a poll with votes - warn user before proceeding
if hasattr(item, 'poll') and item.poll is not None:
votes_count = item.poll.votes_count if hasattr(item.poll, 'votes_count') else 0
if votes_count > 0:
# Show confirmation dialog
warning_title = _("Warning: Poll with votes")
warning_message = _("This post contains a poll with {votes} votes.\n\n"
"According to Mastodon's API, editing this post will reset ALL votes to zero, "
"even if you don't modify the poll itself.\n\n"
"Do you want to continue editing?").format(votes=votes_count)
dialog = wx.MessageDialog(self.buffer, warning_message, warning_title,
wx.YES_NO | wx.NO_DEFAULT | wx.ICON_WARNING)
result = dialog.ShowModal()
dialog.Destroy()
if result != wx.ID_YES:
output.speak(_("Edit cancelled"))
return
# Log item info for debugging
log.debug("Editing status: id={}, has_media_attachments={}, media_count={}".format(
item.id,
hasattr(item, 'media_attachments'),
len(item.media_attachments) if hasattr(item, 'media_attachments') else 0
))
# Create edit dialog with existing post data # Create edit dialog with existing post data
title = _("Edit post") title = _("Edit post")
caption = _("Edit your post here") caption = _("Edit your post here")
@@ -522,7 +545,8 @@ class BaseBuffer(base.Buffer):
if response == wx.ID_OK: if response == wx.ID_OK:
post_data = post.get_data() post_data = post.get_data()
# Call edit_post method in session # 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()) # Note: visibility and language cannot be changed when editing per Mastodon API
call_threaded(self.session.edit_post, post_id=post.post_id, posts=post_data)
if hasattr(post.message, "destroy"): if hasattr(post.message, "destroy"):
post.message.destroy() post.message.destroy()

View File

@@ -2,6 +2,7 @@
import os import os
import re import re
import wx import wx
import logging
import widgetUtils import widgetUtils
import config import config
import output import output
@@ -14,6 +15,8 @@ from wxUI.dialogs.mastodon import postDialogs
from extra.autocompletionUsers import completion from extra.autocompletionUsers import completion
from . import userList from . import userList
log = logging.getLogger("controller.mastodon.messages")
def character_count(post_text, post_cw, character_limit=500): def character_count(post_text, post_cw, character_limit=500):
# We will use text for counting character limit only. # We will use text for counting character limit only.
full_text = post_text+post_cw full_text = post_text+post_cw
@@ -264,7 +267,11 @@ class post(messages.basicMessage):
class editPost(post): class editPost(post):
def __init__(self, session, item, title, caption, *args, **kwargs): def __init__(self, session, item, title, caption, *args, **kwargs):
""" Initialize edit dialog with existing post data. """ """ Initialize edit dialog with existing post data.
Note: Per Mastodon API, visibility and language cannot be changed when editing.
These fields will be displayed but disabled in the UI.
"""
# Extract text from post # Extract text from post
if item.reblog != None: if item.reblog != None:
item = item.reblog item = item.reblog
@@ -276,30 +283,87 @@ class editPost(post):
super(editPost, self).__init__(session, title, caption, text=text, *args, **kwargs) super(editPost, self).__init__(session, title, caption, text=text, *args, **kwargs)
# Store the post ID for editing # Store the post ID for editing
self.post_id = item.id self.post_id = item.id
# Set visibility # Set visibility (read-only, cannot be changed)
visibility_settings = dict(public=0, unlisted=1, private=2, direct=3) visibility_settings = dict(public=0, unlisted=1, private=2, direct=3)
self.message.visibility.SetSelection(visibility_settings.get(item.visibility, 0)) self.message.visibility.SetSelection(visibility_settings.get(item.visibility, 0))
# Set language self.message.visibility.Enable(False) # Disable as it cannot be edited
# Set language (read-only, cannot be changed)
if item.language: if item.language:
self.set_language(item.language) self.set_language(item.language)
self.message.language.Enable(False) # Disable as it cannot be edited
# Set sensitive content and spoiler # Set sensitive content and spoiler
if item.sensitive: if item.sensitive:
self.message.sensitive.SetValue(True) self.message.sensitive.SetValue(True)
if item.spoiler_text: if item.spoiler_text:
self.message.spoiler.ChangeValue(item.spoiler_text) self.message.spoiler.ChangeValue(item.spoiler_text)
self.message.on_sensitivity_changed() self.message.on_sensitivity_changed()
# Load existing media attachments # Load existing poll (if any)
if hasattr(item, 'media_attachments') and len(item.media_attachments) > 0: # Note: You cannot have both media and a poll, so check poll first
for media in item.media_attachments: if hasattr(item, 'poll') and item.poll is not None:
media_info = { log.debug("Loading existing poll for post {}".format(self.post_id))
"id": media.id, # Keep the existing media ID poll = item.poll
"type": media.type, # Extract poll options (just the text, not the votes)
"file": media.url, # URL of existing media poll_options = [option.title for option in poll.options]
"description": media.description or "" # Calculate expires_in based on current time and expires_at
} # For editing, we need to provide a new expiration time
self.attachments.append(media_info) # Since we can't get the original expires_in, use a default or let user configure
# Display in the attachment list # For now, use 1 day (86400 seconds) as default
self.message.add_item(item=[media.url.split('/')[-1], media.type, media.description or ""]) expires_in = 86400
if hasattr(poll, 'expires_at') and poll.expires_at and not poll.expired:
# Calculate remaining time if poll hasn't expired
from dateutil import parser as date_parser
import datetime
try:
expires_at = poll.expires_at
if isinstance(expires_at, str):
expires_at = date_parser.parse(expires_at)
now = datetime.datetime.now(datetime.timezone.utc)
remaining = (expires_at - now).total_seconds()
if remaining > 0:
expires_in = int(remaining)
except Exception as e:
log.warning("Could not calculate poll expiration: {}".format(e))
poll_info = {
"type": "poll",
"file": "",
"description": _("Poll with {} options").format(len(poll_options)),
"options": poll_options,
"expires_in": expires_in,
"multiple": poll.multiple if hasattr(poll, 'multiple') else False,
"hide_totals": poll.voters_count == 0 if hasattr(poll, 'voters_count') else False
}
self.attachments.append(poll_info)
self.message.add_item(item=[poll_info["file"], poll_info["type"], poll_info["description"]])
log.debug("Loaded poll with {} options. WARNING: Editing will reset all votes!".format(len(poll_options)))
# Load existing media attachments (only if no poll)
elif hasattr(item, 'media_attachments'):
log.debug("Loading existing media attachments for post {}".format(self.post_id))
log.debug("Item has media_attachments attribute, count: {}".format(len(item.media_attachments)))
if len(item.media_attachments) > 0:
for media in item.media_attachments:
log.debug("Processing media: id={}, type={}, url={}".format(media.id, media.type, media.url))
media_info = {
"id": media.id, # Keep the existing media ID
"type": media.type,
"file": media.url, # URL of existing media
"description": media.description or ""
}
# Include focus point if available
if hasattr(media, 'meta') and media.meta and 'focus' in media.meta:
focus = media.meta['focus']
media_info["focus"] = (focus.get('x'), focus.get('y'))
log.debug("Added focus point: {}".format(media_info["focus"]))
self.attachments.append(media_info)
# Display in the attachment list
display_name = media.url.split('/')[-1]
log.debug("Adding item to UI: name={}, type={}, desc={}".format(display_name, media.type, media.description or ""))
self.message.add_item(item=[display_name, media.type, media.description or ""])
log.debug("Total attachments loaded: {}".format(len(self.attachments)))
else:
log.debug("media_attachments list is empty")
else:
log.debug("Item has no poll or media attachments")
# Update text processor to reflect the loaded content # Update text processor to reflect the loaded content
self.text_processor() self.text_processor()