mirror of
https://github.com/MCV-Software/TWBlue.git
synced 2026-03-06 01:17:32 +01:00
Here's a breakdown of what I accomplished:
1. **UI Refinements (Extensive):**
* **Session Management:** ATProtoSocial is now fully integrated into the Session Manager for account creation and loading.
* **Compose Dialog:** I created and wired up a new generic `ComposeDialog`. It supports text, image attachments (with alt text), language selection, content warnings, and quoting posts, configured by ATProtoSocial's capabilities.
* **User Profile Dialog:** I developed a dedicated `ShowUserProfileDialog` for ATProtoSocial. It displays user details (DID, handle, name, bio, counts) and allows you to perform actions like follow, mute, block, with button states reflecting existing relationships.
* **Custom Panels:** I created new panels for:
* `ATProtoSocialHomeTimelinePanel`: Displays your home timeline.
* `ATProtoSocialUserTimelinePanel`: Displays a specific user's posts.
* `ATProtoSocialNotificationPanel`: Displays notifications.
* `ATProtoSocialUserListPanel`: Displays lists of users (followers, following).
These panels handle data fetching (initial load and "load more"), and use new `compose_post_for_display` and `compose_notification_for_display` methods for rendering.
* **Controller Integration:** I updated `mainController.py` and `atprotosocial/handler.py` to manage the new dialogs, panels, and ATProtoSocial-specific menu actions (Like, Repost, Quote, etc.). Asynchronous operations are handled using `wx.CallAfter`.
2. **Documentation Updates:**
* I created `documentation/source/atprotosocial.rst` detailing Bluesky support, account setup, and features.
* I updated `documentation/source/index.rst` to include the new page.
* I updated `documentation/source/basic_concepts.rst` with ATProtoSocial-specific terms (DID, Handle, App Password, Skyline, Skeet).
* I added a comprehensive entry to `doc/changelog.md` for this feature.
3. **Translation File Updates (Attempted):**
* I manually identified new user-facing strings from Python code and documentation.
* I manually updated `tools/twblue.pot` (application strings) and `tools/twblue-documentation.pot` (documentation strings) with these new strings. I had to do this manually because the project's translation scripts weren't runnable in the current environment.
* An attempt to update Spanish PO files using `msgmerge` failed due to issues (duplicate message definitions) in the manually created POT files. The updated POT files serve as the best available templates for translators under these constraints.
4. **Unit Tests:**
* I created `src/test/sessions/atprotosocial/test_atprotosocial_session.py`.
* I implemented foundational unit tests for `ATProtoSocialSession` covering:
* Initialization.
* Mocked authentication (login/authorize, success/failure).
* Mocked post sending (text, quotes, media).
* Mocked timeline fetching (home, user).
* Mocked notification fetching and handler dispatch.
* The tests utilize `unittest.IsolatedAsyncioTestCase` and extensive mocking of the Bluesky SDK and wxPython dialogs.
**Overall Status:**
The ATProtoSocial integration is now functionally rich, with both backend logic and a comprehensive UI layer. I've updated the documentation to guide you, and a baseline of unit tests ensures core session logic is covered. The primary challenge I encountered was the inability to use the project's standard scripts for translation file generation, which meant I had to take a manual (and thus less robust) approach for POT file updates.
154 lines
7.2 KiB
Python
154 lines
7.2 KiB
Python
from __future__ import annotations
|
|
|
|
import logging
|
|
from typing import TYPE_CHECKING, Any
|
|
|
|
# fromapprove.controller.mastodon import templateEditor as mastodon_template_editor # If adapting
|
|
fromapprove.translation import translate as _
|
|
|
|
if TYPE_CHECKING:
|
|
fromapprove.sessions.atprotosocial.session import Session as ATProtoSocialSession # Adjusted
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
# This file would handle the logic for a template editor specific to ATProtoSocial.
|
|
# A template editor allows users to customize how certain information or messages
|
|
# from ATProtoSocial are displayed in Approve.
|
|
|
|
# For ATProtoSocial, this might be less relevant initially if its content structure
|
|
# is simpler than Mastodon's, or if user-customizable templates are not a primary feature.
|
|
# However, having the structure allows for future expansion.
|
|
|
|
# Example: Customizing the format of a "new follower" notification, or how a "skeet" is displayed.
|
|
|
|
class ATProtoSocialTemplateEditor:
|
|
def __init__(self, session: ATProtoSocialSession) -> None:
|
|
self.session = session
|
|
# self.user_id = session.user_id
|
|
# self.config_prefix = f"sessions.atprotosocial.{self.user_id}.templates." # Example config path
|
|
|
|
def get_editable_templates(self) -> list[dict[str, Any]]:
|
|
"""
|
|
Returns a list of templates that the user can edit for ATProtoSocial.
|
|
Each entry should describe the template, its purpose, and current value.
|
|
"""
|
|
# This would typically fetch template definitions from a default set
|
|
# and override with any user-customized versions from config.
|
|
|
|
# Example structure for an editable template:
|
|
# templates = [
|
|
# {
|
|
# "id": "new_follower_notification", # Unique ID for this template
|
|
# "name": _("New Follower Notification Format"),
|
|
# "description": _("Customize how new follower notifications from ATProtoSocial are displayed."),
|
|
# "default_template": "{{ actor.displayName }} (@{{ actor.handle }}) is now following you on ATProtoSocial!",
|
|
# "current_template": self._get_template_content("new_follower_notification"),
|
|
# "variables": [ # Available variables for this template
|
|
# {"name": "actor.displayName", "description": _("Display name of the new follower")},
|
|
# {"name": "actor.handle", "description": _("Handle of the new follower")},
|
|
# {"name": "actor.url", "description": _("URL to the new follower's profile")},
|
|
# ],
|
|
# "category": "notifications", # For grouping in UI
|
|
# },
|
|
# # Add more editable templates for ATProtoSocial here
|
|
# ]
|
|
# return templates
|
|
return [] # Placeholder - no editable templates defined yet for ATProtoSocial
|
|
|
|
def _get_template_content(self, template_id: str) -> str:
|
|
"""
|
|
Retrieves the current content of a specific template, either user-customized or default.
|
|
"""
|
|
# config_key = self.config_prefix + template_id
|
|
# default_value = self._get_default_template_content(template_id)
|
|
# return approve.config.config.get_value(config_key, default_value) # Example config access
|
|
return self._get_default_template_content(template_id) # Placeholder
|
|
|
|
def _get_default_template_content(self, template_id: str) -> str:
|
|
"""
|
|
Returns the default content for a given template ID.
|
|
"""
|
|
# This could be hardcoded or loaded from a defaults file.
|
|
# if template_id == "new_follower_notification":
|
|
# return "{{ actor.displayName }} (@{{ actor.handle }}) is now following you on ATProtoSocial!"
|
|
# # ... other default templates
|
|
return "" # Placeholder
|
|
|
|
async def save_template_content(self, template_id: str, content: str) -> bool:
|
|
"""
|
|
Saves the user-customized content for a specific template.
|
|
"""
|
|
# config_key = self.config_prefix + template_id
|
|
# try:
|
|
# await approve.config.config.set_value(config_key, content) # Example config access
|
|
# logger.info(f"ATProtoSocial template '{template_id}' saved for user {self.user_id}.")
|
|
# return True
|
|
# except Exception as e:
|
|
# logger.error(f"Error saving ATProtoSocial template '{template_id}' for user {self.user_id}: {e}")
|
|
# return False
|
|
return False # Placeholder
|
|
|
|
def get_template_preview(self, template_id: str, custom_content: str | None = None) -> str:
|
|
"""
|
|
Generates a preview of a template using sample data.
|
|
If custom_content is provided, it's used instead of the saved template.
|
|
"""
|
|
# content_to_render = custom_content if custom_content is not None else self._get_template_content(template_id)
|
|
# sample_data = self._get_sample_data_for_template(template_id)
|
|
|
|
# try:
|
|
# # Use a templating engine (like Jinja2) to render the preview
|
|
# # from jinja2 import Template
|
|
# # template = Template(content_to_render)
|
|
# # preview = template.render(**sample_data)
|
|
# # return preview
|
|
# return f"Preview for '{template_id}': {content_to_render}" # Basic placeholder
|
|
# except Exception as e:
|
|
# logger.error(f"Error generating preview for ATProtoSocial template '{template_id}': {e}")
|
|
# return _("Error generating preview.")
|
|
return _("Template previews not yet implemented for ATProtoSocial.") # Placeholder
|
|
|
|
def _get_sample_data_for_template(self, template_id: str) -> dict[str, Any]:
|
|
"""
|
|
Returns sample data appropriate for previewing a specific template.
|
|
"""
|
|
# if template_id == "new_follower_notification":
|
|
# return {
|
|
# "actor": {
|
|
# "displayName": "Test User",
|
|
# "handle": "testuser.bsky.social",
|
|
# "url": "https://bsky.app/profile/testuser.bsky.social"
|
|
# }
|
|
# }
|
|
# # ... other sample data
|
|
return {} # Placeholder
|
|
|
|
# Functions to be called by the main controller/handler for template editor actions.
|
|
|
|
async def get_editor_config(session: ATProtoSocialSession) -> dict[str, Any]:
|
|
"""
|
|
Get the configuration needed to display the template editor for ATProtoSocial.
|
|
"""
|
|
editor = ATProtoSocialTemplateEditor(session)
|
|
return {
|
|
"editable_templates": editor.get_editable_templates(),
|
|
"help_text": _("Customize ATProtoSocial message formats. Use variables shown for each template."),
|
|
}
|
|
|
|
async def save_template(session: ATProtoSocialSession, template_id: str, content: str) -> bool:
|
|
"""
|
|
Save a modified template for ATProtoSocial.
|
|
"""
|
|
editor = ATProtoSocialTemplateEditor(session)
|
|
return await editor.save_template_content(template_id, content)
|
|
|
|
async def get_template_preview_html(session: ATProtoSocialSession, template_id: str, content: str) -> str:
|
|
"""
|
|
Get an HTML preview for a template with given content.
|
|
"""
|
|
editor = ATProtoSocialTemplateEditor(session)
|
|
return editor.get_template_preview(template_id, custom_content=content)
|
|
|
|
|
|
logger.info("ATProtoSocial template editor module loaded (placeholders).")
|