mirror of
https://github.com/MCV-Software/TWBlue.git
synced 2026-03-06 09:27:33 +01:00
Refactor y eliminar patrones raros.
This commit is contained in:
@@ -98,6 +98,24 @@ def format_error_message(error_description: str, details: str | None = None) ->
|
|||||||
logger.info("Blueski messages module loaded (placeholders).")
|
logger.info("Blueski messages module loaded (placeholders).")
|
||||||
|
|
||||||
|
|
||||||
|
class post(base_messages.basicMessage):
|
||||||
|
def __init__(self, session: Any, title: str, caption: str, text: str = "", *args, **kwargs):
|
||||||
|
self.session = session
|
||||||
|
self.title = title
|
||||||
|
self.message = postDialogs.Post(caption=caption, text=text, *args, **kwargs)
|
||||||
|
try:
|
||||||
|
self.message.SetTitle(title)
|
||||||
|
self.message.text.SetInsertionPoint(len(self.message.text.GetValue()))
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def get_data(self):
|
||||||
|
return self.message.get_payload()
|
||||||
|
|
||||||
|
def text_processor(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
def _g(obj: Any, key: str, default: Any = None) -> Any:
|
def _g(obj: Any, key: str, default: Any = None) -> Any:
|
||||||
if isinstance(obj, dict):
|
if isinstance(obj, dict):
|
||||||
return obj.get(key, default)
|
return obj.get(key, default)
|
||||||
|
|||||||
@@ -190,28 +190,20 @@ class BaseBuffer(base.Buffer):
|
|||||||
pub.sendMessage("execute-action", action="copy_to_clipboard")
|
pub.sendMessage("execute-action", action="copy_to_clipboard")
|
||||||
|
|
||||||
def on_post(self, evt):
|
def on_post(self, evt):
|
||||||
from wxUI.dialogs.blueski import postDialogs
|
dlg = blueski_messages.post(session=self.session, title=_("New Post"), caption=_("New Post"))
|
||||||
dlg = postDialogs.Post(caption=_("New Post"))
|
if dlg.message.ShowModal() == wx.ID_OK:
|
||||||
if dlg.ShowModal() == wx.ID_OK:
|
text, files, cw, langs = dlg.get_data()
|
||||||
text, files, cw, langs = dlg.get_payload()
|
self._send_post_async(
|
||||||
if not text and not files:
|
text=text,
|
||||||
dlg.Destroy()
|
files=files,
|
||||||
return
|
cw_text=cw,
|
||||||
def do_send():
|
langs=langs,
|
||||||
try:
|
success_message=_("Sent."),
|
||||||
uri = self.session.send_message(message=text, files=files, cw_text=cw, langs=langs)
|
error_message=_("An error occurred while posting."),
|
||||||
if uri:
|
sound="tweet_send.ogg",
|
||||||
wx.CallAfter(self.session.sound.play, "tweet_send.ogg")
|
refresh_args=(False, False),
|
||||||
wx.CallAfter(output.speak, _("Sent."))
|
)
|
||||||
if hasattr(self, "start_stream"):
|
dlg.message.Destroy()
|
||||||
wx.CallAfter(self.start_stream, False, False)
|
|
||||||
else:
|
|
||||||
wx.CallAfter(output.speak, _("Failed to send post."), True)
|
|
||||||
except Exception:
|
|
||||||
log.exception("Error sending Bluesky post")
|
|
||||||
wx.CallAfter(output.speak, _("An error occurred while posting."), True)
|
|
||||||
call_threaded(do_send)
|
|
||||||
dlg.Destroy()
|
|
||||||
|
|
||||||
def on_reply(self, evt):
|
def on_reply(self, evt):
|
||||||
item = self.get_item()
|
item = self.get_item()
|
||||||
@@ -237,38 +229,71 @@ class BaseBuffer(base.Buffer):
|
|||||||
handle = g(author, "handle", "")
|
handle = g(author, "handle", "")
|
||||||
initial_text = f"@{handle} " if handle and not handle.startswith("@") else (f"{handle} " if handle else "")
|
initial_text = f"@{handle} " if handle and not handle.startswith("@") else (f"{handle} " if handle else "")
|
||||||
|
|
||||||
from wxUI.dialogs.blueski import postDialogs
|
dlg = blueski_messages.post(session=self.session, title=_("Reply"), caption=_("Reply"), text=initial_text)
|
||||||
dlg = postDialogs.Post(caption=_("Reply"), text=initial_text)
|
if dlg.message.ShowModal() == wx.ID_OK:
|
||||||
if dlg.ShowModal() == wx.ID_OK:
|
text, files, cw, langs = dlg.get_data()
|
||||||
text, files, cw, langs = dlg.get_payload()
|
refresh_args = (True, False) if getattr(self, "type", "") == "conversation" else None
|
||||||
if not text and not files:
|
self._send_post_async(
|
||||||
dlg.Destroy()
|
text=text,
|
||||||
return
|
files=files,
|
||||||
def do_send():
|
cw_text=cw,
|
||||||
try:
|
langs=langs,
|
||||||
uri_resp = self.session.send_message(
|
reply_to=uri,
|
||||||
message=text,
|
reply_to_cid=reply_cid,
|
||||||
files=files,
|
success_message=_("Reply sent."),
|
||||||
reply_to=uri,
|
error_message=_("An error occurred while replying."),
|
||||||
reply_to_cid=reply_cid,
|
sound="reply_send.ogg",
|
||||||
cw_text=cw,
|
refresh_args=refresh_args,
|
||||||
langs=langs
|
)
|
||||||
)
|
dlg.message.Destroy()
|
||||||
if uri_resp:
|
|
||||||
wx.CallAfter(self.session.sound.play, "reply_send.ogg")
|
def _send_post_async(
|
||||||
wx.CallAfter(output.speak, _("Reply sent."))
|
self,
|
||||||
if getattr(self, "type", "") == "conversation":
|
*,
|
||||||
try:
|
text,
|
||||||
wx.CallAfter(self.start_stream, True, False)
|
files,
|
||||||
except Exception:
|
cw_text,
|
||||||
pass
|
langs,
|
||||||
else:
|
reply_to=None,
|
||||||
wx.CallAfter(output.speak, _("Failed to send reply."), True)
|
reply_to_cid=None,
|
||||||
except Exception:
|
success_message="",
|
||||||
log.exception("Error sending Bluesky reply")
|
error_message="",
|
||||||
wx.CallAfter(output.speak, _("An error occurred while replying."), True)
|
sound=None,
|
||||||
call_threaded(do_send)
|
refresh_args=None,
|
||||||
dlg.Destroy()
|
):
|
||||||
|
if not text and not files:
|
||||||
|
return
|
||||||
|
|
||||||
|
def do_send():
|
||||||
|
try:
|
||||||
|
uri_resp = self.session.send_message(
|
||||||
|
message=text,
|
||||||
|
files=files,
|
||||||
|
reply_to=reply_to,
|
||||||
|
reply_to_cid=reply_to_cid,
|
||||||
|
cw_text=cw_text,
|
||||||
|
langs=langs,
|
||||||
|
)
|
||||||
|
if uri_resp:
|
||||||
|
if sound:
|
||||||
|
wx.CallAfter(self.session.sound.play, sound)
|
||||||
|
if success_message:
|
||||||
|
wx.CallAfter(output.speak, success_message)
|
||||||
|
if refresh_args and hasattr(self, "start_stream"):
|
||||||
|
try:
|
||||||
|
wx.CallAfter(self.start_stream, *refresh_args)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
wx.CallAfter(output.speak, _("Failed to send post."), True)
|
||||||
|
except Exception:
|
||||||
|
log.exception("Error sending Bluesky post")
|
||||||
|
if error_message:
|
||||||
|
wx.CallAfter(output.speak, error_message, True)
|
||||||
|
else:
|
||||||
|
wx.CallAfter(output.speak, _("An error occurred while posting."), True)
|
||||||
|
|
||||||
|
call_threaded(do_send)
|
||||||
|
|
||||||
def on_repost(self, evt):
|
def on_repost(self, evt):
|
||||||
self.share_item(confirm=True)
|
self.share_item(confirm=True)
|
||||||
|
|||||||
@@ -6,3 +6,4 @@ from .users import UserBuffer
|
|||||||
from .notifications import NotificationsBuffer
|
from .notifications import NotificationsBuffer
|
||||||
from .search import SearchBuffer
|
from .search import SearchBuffer
|
||||||
from .community import CommunityBuffer
|
from .community import CommunityBuffer
|
||||||
|
from .announcements import AnnouncementsBuffer
|
||||||
165
srcantiguo/controller/buffers/mastodon/announcements.py
Normal file
165
srcantiguo/controller/buffers/mastodon/announcements.py
Normal file
@@ -0,0 +1,165 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import time
|
||||||
|
import logging
|
||||||
|
import arrow
|
||||||
|
import widgetUtils
|
||||||
|
import wx
|
||||||
|
import output
|
||||||
|
import languageHandler
|
||||||
|
import config
|
||||||
|
from pubsub import pub
|
||||||
|
from controller.buffers.mastodon.base import BaseBuffer
|
||||||
|
from sessions.mastodon import compose, templates
|
||||||
|
from wxUI import buffers
|
||||||
|
from wxUI.dialogs.mastodon import menus
|
||||||
|
from mysc.thread_utils import call_threaded
|
||||||
|
|
||||||
|
log = logging.getLogger("controller.buffers.mastodon.announcements")
|
||||||
|
|
||||||
|
class AnnouncementsBuffer(BaseBuffer):
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
# We enforce compose_func="compose_announcement"
|
||||||
|
kwargs["compose_func"] = "compose_announcement"
|
||||||
|
super(AnnouncementsBuffer, self).__init__(*args, **kwargs)
|
||||||
|
self.type = "announcementsBuffer"
|
||||||
|
|
||||||
|
def create_buffer(self, parent, name):
|
||||||
|
self.buffer = buffers.mastodon.announcementsPanel(parent, name)
|
||||||
|
|
||||||
|
def get_buffer_name(self):
|
||||||
|
return _("Announcements")
|
||||||
|
|
||||||
|
def bind_events(self):
|
||||||
|
self.buffer.set_focus_function(self.onFocus)
|
||||||
|
widgetUtils.connect_event(self.buffer.list.list, widgetUtils.KEYPRESS, self.get_event)
|
||||||
|
widgetUtils.connect_event(self.buffer, widgetUtils.BUTTON_PRESSED, self.dismiss_announcement, self.buffer.dismiss)
|
||||||
|
widgetUtils.connect_event(self.buffer.list.list, wx.EVT_LIST_ITEM_RIGHT_CLICK, self.show_menu)
|
||||||
|
widgetUtils.connect_event(self.buffer.list.list, wx.EVT_LIST_KEY_DOWN, self.show_menu_by_key)
|
||||||
|
|
||||||
|
def dismiss_announcement(self, event=None, item=None, *args, **kwargs):
|
||||||
|
index = self.buffer.list.get_selected()
|
||||||
|
if index == -1: return
|
||||||
|
item = self.session.db[self.name][index]
|
||||||
|
|
||||||
|
# Optimistic UI update or wait for API? Let's wait for API to be safe, but run threaded.
|
||||||
|
# We need a custom call because 'announcements_dismiss' returns None on success usually.
|
||||||
|
def _do_dismiss():
|
||||||
|
try:
|
||||||
|
self.session.api_call(call_name="announcements_dismiss", id=str(item.id))
|
||||||
|
# If success, update UI in main thread
|
||||||
|
wx.CallAfter(self._on_dismiss_success, index)
|
||||||
|
except Exception as e:
|
||||||
|
log.exception("Error dismissing announcement")
|
||||||
|
self.session.sound.play("error.ogg")
|
||||||
|
|
||||||
|
call_threaded(_do_dismiss)
|
||||||
|
|
||||||
|
def _on_dismiss_success(self, index):
|
||||||
|
if index < len(self.session.db[self.name]):
|
||||||
|
self.session.db[self.name].pop(index)
|
||||||
|
self.buffer.list.remove_item(index)
|
||||||
|
output.speak(_("Announcement dismissed."))
|
||||||
|
|
||||||
|
def show_menu(self, ev, pos=0, *args, **kwargs):
|
||||||
|
if self.buffer.list.get_count() == 0:
|
||||||
|
return
|
||||||
|
# Create a simple menu
|
||||||
|
menu = wx.Menu()
|
||||||
|
dismiss_item = menu.Append(wx.ID_ANY, _("Dismiss"))
|
||||||
|
copy_item = menu.Append(wx.ID_ANY, _("Copy text"))
|
||||||
|
|
||||||
|
self.buffer.Bind(wx.EVT_MENU, self.dismiss_announcement, dismiss_item)
|
||||||
|
self.buffer.Bind(wx.EVT_MENU, self.copy, copy_item)
|
||||||
|
|
||||||
|
if pos != 0:
|
||||||
|
self.buffer.PopupMenu(menu, pos)
|
||||||
|
else:
|
||||||
|
self.buffer.PopupMenu(menu, self.buffer.list.list.GetPosition())
|
||||||
|
|
||||||
|
def url(self, *args, **kwargs):
|
||||||
|
self.dismiss_announcement()
|
||||||
|
|
||||||
|
def get_more_items(self): output.speak(_("This buffer does not support loading more items."), True)
|
||||||
|
|
||||||
|
# Disable social interactions not applicable to announcements
|
||||||
|
def reply(self, *args, **kwargs):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def share_item(self, *args, **kwargs):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def toggle_favorite(self, *args, **kwargs):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def add_to_favorites(self, *args, **kwargs):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def remove_from_favorites(self, *args, **kwargs):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def toggle_bookmark(self, *args, **kwargs):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def mute_conversation(self, *args, **kwargs):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def vote(self, *args, **kwargs):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def send_message(self, *args, **kwargs):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def user_details(self, *args, **kwargs):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def view_item(self, *args, **kwargs):
|
||||||
|
# We could implement a specific viewer for announcements if needed,
|
||||||
|
# but the default one expects a status object structure.
|
||||||
|
pass
|
||||||
|
|
||||||
|
def copy(self, event=None):
|
||||||
|
item = self.get_item()
|
||||||
|
if item:
|
||||||
|
pub.sendMessage("execute-action", action="copy_to_clipboard")
|
||||||
|
|
||||||
|
def onFocus(self, *args, **kwargs):
|
||||||
|
# Similar logic to BaseBuffer but adapted if needed.
|
||||||
|
# BaseBuffer.onFocus handles reading long posts.
|
||||||
|
if config.app["app-settings"]["read_long_posts_in_gui"] == True and self.buffer.list.list.HasFocus():
|
||||||
|
wx.CallLater(40, output.speak, self.get_message(), interrupt=True)
|
||||||
|
|
||||||
|
def get_message(self):
|
||||||
|
# Override to use announcement template
|
||||||
|
announcement = self.get_item()
|
||||||
|
if announcement == None:
|
||||||
|
return
|
||||||
|
template = self.session.settings.get("templates", {}).get("announcement", templates.announcement_default_template)
|
||||||
|
t = templates.render_announcement(announcement, template, self.session.settings, relative_times=self.session.settings["general"]["relative_times"], offset_hours=self.session.db["utc_offset"])
|
||||||
|
return t
|
||||||
|
|
||||||
|
def start_stream(self, mandatory=False, play_sound=True, avoid_autoreading=False):
|
||||||
|
current_time = time.time()
|
||||||
|
if self.execution_time == 0 or current_time-self.execution_time >= 300 or mandatory==True:
|
||||||
|
self.execution_time = current_time
|
||||||
|
log.debug("Starting stream for announcements buffer")
|
||||||
|
try:
|
||||||
|
# The announcements API does not accept min_id or limit parameters
|
||||||
|
results = self.session.api.announcements()
|
||||||
|
# Reverse the list so order_buffer processes them according to user preference
|
||||||
|
results.reverse()
|
||||||
|
except Exception as e:
|
||||||
|
log.exception("Error retrieving announcements: %s" % (str(e)))
|
||||||
|
return 0
|
||||||
|
|
||||||
|
# order_buffer handles duplication filtering by ID internally
|
||||||
|
number_of_items = self.session.order_buffer(self.name, results)
|
||||||
|
log.debug("Number of new announcements retrieved: %d" % (number_of_items,))
|
||||||
|
|
||||||
|
self.put_items_on_list(number_of_items)
|
||||||
|
|
||||||
|
if number_of_items > 0 and play_sound == True and self.sound != None and self.session.settings["sound"]["session_mute"] == False:
|
||||||
|
self.session.sound.play(self.sound)
|
||||||
|
|
||||||
|
return number_of_items
|
||||||
|
return 0
|
||||||
@@ -126,14 +126,16 @@ class BaseBuffer(base.Buffer):
|
|||||||
min_id = None
|
min_id = None
|
||||||
# toDo: Implement reverse timelines properly here.
|
# toDo: Implement reverse timelines properly here.
|
||||||
if (self.name != "favorites" and self.name != "bookmarks") and self.name in self.session.db and len(self.session.db[self.name]) > 0:
|
if (self.name != "favorites" and self.name != "bookmarks") and self.name in self.session.db and len(self.session.db[self.name]) > 0:
|
||||||
if self.session.settings["general"]["reverse_timelines"]:
|
# We use the maximum ID present in the buffer to ensure we only request posts
|
||||||
min_id = self.session.db[self.name][0].id
|
# that are newer than our most recent chronological post.
|
||||||
else:
|
# This prevents old pinned posts from pulling in hundreds of previous statuses.
|
||||||
min_id = self.session.db[self.name][-1].id
|
min_id = max(item.id for item in self.session.db[self.name])
|
||||||
# loads pinned posts from user accounts.
|
# loads pinned posts from user accounts.
|
||||||
# Load those posts only when there are no items previously loaded.
|
# Load those posts only when there are no items previously loaded.
|
||||||
if "-timeline" in self.name and "account_statuses" in self.function and len(self.session.db.get(self.name, [])) == 0:
|
if "-timeline" in self.name and "account_statuses" in self.function and len(self.session.db.get(self.name, [])) == 0:
|
||||||
pinned_posts = self.session.api.account_statuses(pinned=True, limit=count, *self.args, **self.kwargs)
|
pinned_posts = self.session.api.account_statuses(pinned=True, limit=count, *self.args, **self.kwargs)
|
||||||
|
for p in pinned_posts:
|
||||||
|
p["pinned"] = True
|
||||||
pinned_posts.reverse()
|
pinned_posts.reverse()
|
||||||
else:
|
else:
|
||||||
pinned_posts = None
|
pinned_posts = None
|
||||||
@@ -182,10 +184,17 @@ class BaseBuffer(base.Buffer):
|
|||||||
|
|
||||||
def get_more_items(self):
|
def get_more_items(self):
|
||||||
elements = []
|
elements = []
|
||||||
if self.session.settings["general"]["reverse_timelines"] == False:
|
if len(self.session.db[self.name]) == 0:
|
||||||
max_id = self.session.db[self.name][0].id
|
return
|
||||||
|
# We use the minimum ID in the buffer to correctly request the next page of older items.
|
||||||
|
# This prevents old pinned posts from causing us to skip chronological posts.
|
||||||
|
# We try to exclude pinned posts from this calculation as they are usually outliers at the top.
|
||||||
|
unpinned_ids = [item.id for item in self.session.db[self.name] if not getattr(item, "pinned", False)]
|
||||||
|
if unpinned_ids:
|
||||||
|
max_id = min(unpinned_ids)
|
||||||
else:
|
else:
|
||||||
max_id = self.session.db[self.name][-1].id
|
max_id = min(item.id for item in self.session.db[self.name])
|
||||||
|
|
||||||
try:
|
try:
|
||||||
items = getattr(self.session.api, self.function)(max_id=max_id, limit=self.session.settings["general"]["max_posts_per_call"], *self.args, **self.kwargs)
|
items = getattr(self.session.api, self.function)(max_id=max_id, limit=self.session.settings["general"]["max_posts_per_call"], *self.args, **self.kwargs)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -311,8 +320,10 @@ class BaseBuffer(base.Buffer):
|
|||||||
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)
|
||||||
|
widgetUtils.connect_event(menu, widgetUtils.MENU, self.quote, menuitem=menu.quote)
|
||||||
else:
|
else:
|
||||||
menu.boost.Enable(False)
|
menu.boost.Enable(False)
|
||||||
|
menu.quote.Enable(False)
|
||||||
widgetUtils.connect_event(menu, widgetUtils.MENU, self.fav, menuitem=menu.fav)
|
widgetUtils.connect_event(menu, widgetUtils.MENU, self.fav, menuitem=menu.fav)
|
||||||
widgetUtils.connect_event(menu, widgetUtils.MENU, self.unfav, menuitem=menu.unfav)
|
widgetUtils.connect_event(menu, widgetUtils.MENU, self.unfav, menuitem=menu.unfav)
|
||||||
widgetUtils.connect_event(menu, widgetUtils.MENU, self.mute_conversation, menuitem=menu.mute)
|
widgetUtils.connect_event(menu, widgetUtils.MENU, self.mute_conversation, menuitem=menu.mute)
|
||||||
@@ -442,11 +453,30 @@ class BaseBuffer(base.Buffer):
|
|||||||
id = item.id
|
id = item.id
|
||||||
if self.session.settings["general"]["boost_mode"] == "ask":
|
if self.session.settings["general"]["boost_mode"] == "ask":
|
||||||
answer = mastodon_dialogs.boost_question()
|
answer = mastodon_dialogs.boost_question()
|
||||||
if answer == True:
|
if answer == 1:
|
||||||
self._direct_boost(id)
|
self._direct_boost(id)
|
||||||
|
elif answer == 2:
|
||||||
|
self.quote(item=item)
|
||||||
else:
|
else:
|
||||||
self._direct_boost(id)
|
self._direct_boost(id)
|
||||||
|
|
||||||
|
def quote(self, event=None, item=None, *args, **kwargs):
|
||||||
|
if item == None:
|
||||||
|
item = self.get_item()
|
||||||
|
if self.can_share(item=item) == False:
|
||||||
|
return output.speak(_("This action is not supported on conversations."))
|
||||||
|
|
||||||
|
title = _("Quote post")
|
||||||
|
caption = _("Write your comment here")
|
||||||
|
post = messages.post(session=self.session, title=title, caption=caption)
|
||||||
|
|
||||||
|
response = post.message.ShowModal()
|
||||||
|
if response == wx.ID_OK:
|
||||||
|
post_data = post.get_data()
|
||||||
|
call_threaded(self.session.send_post, quote_id=item.id, posts=post_data, visibility=post.get_visibility(), language=post.get_language(), **kwargs)
|
||||||
|
if hasattr(post.message, "destroy"):
|
||||||
|
post.message.destroy()
|
||||||
|
|
||||||
def _direct_boost(self, id):
|
def _direct_boost(self, id):
|
||||||
item = self.session.api_call(call_name="status_reblog", _sound="retweet_send.ogg", id=id)
|
item = self.session.api_call(call_name="status_reblog", _sound="retweet_send.ogg", id=id)
|
||||||
|
|
||||||
|
|||||||
@@ -33,10 +33,7 @@ class CommunityBuffer(base.BaseBuffer):
|
|||||||
min_id = None
|
min_id = None
|
||||||
# toDo: Implement reverse timelines properly here.
|
# toDo: Implement reverse timelines properly here.
|
||||||
if self.name in self.session.db and len(self.session.db[self.name]) > 0:
|
if self.name in self.session.db and len(self.session.db[self.name]) > 0:
|
||||||
if self.session.settings["general"]["reverse_timelines"]:
|
min_id = max(item.id for item in self.session.db[self.name])
|
||||||
min_id = self.session.db[self.name][0].id
|
|
||||||
else:
|
|
||||||
min_id = self.session.db[self.name][-1].id
|
|
||||||
try:
|
try:
|
||||||
results = self.community_api.timeline(timeline=self.timeline, min_id=min_id, limit=count, *self.args, **self.kwargs)
|
results = self.community_api.timeline(timeline=self.timeline, min_id=min_id, limit=count, *self.args, **self.kwargs)
|
||||||
results.reverse()
|
results.reverse()
|
||||||
@@ -55,10 +52,15 @@ class CommunityBuffer(base.BaseBuffer):
|
|||||||
|
|
||||||
def get_more_items(self):
|
def get_more_items(self):
|
||||||
elements = []
|
elements = []
|
||||||
if self.session.settings["general"]["reverse_timelines"] == False:
|
if len(self.session.db[self.name]) == 0:
|
||||||
max_id = self.session.db[self.name][0].id
|
return
|
||||||
|
|
||||||
|
unpinned_ids = [item.id for item in self.session.db[self.name] if not getattr(item, "pinned", False)]
|
||||||
|
if unpinned_ids:
|
||||||
|
max_id = min(unpinned_ids)
|
||||||
else:
|
else:
|
||||||
max_id = self.session.db[self.name][-1].id
|
max_id = min(item.id for item in self.session.db[self.name])
|
||||||
|
|
||||||
try:
|
try:
|
||||||
items = self.community_api.timeline(timeline=self.timeline, max_id=max_id, limit=self.session.settings["general"]["max_posts_per_call"], *self.args, **self.kwargs)
|
items = self.community_api.timeline(timeline=self.timeline, max_id=max_id, limit=self.session.settings["general"]["max_posts_per_call"], *self.args, **self.kwargs)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|||||||
@@ -42,10 +42,22 @@ class MentionsBuffer(BaseBuffer):
|
|||||||
|
|
||||||
def get_more_items(self):
|
def get_more_items(self):
|
||||||
elements = []
|
elements = []
|
||||||
if self.session.settings["general"]["reverse_timelines"] == False:
|
if len(self.session.db[self.name]) == 0:
|
||||||
max_id = self.session.db[self.name][0].id
|
return
|
||||||
else:
|
|
||||||
max_id = self.session.db[self.name][-1].id
|
# In mentions buffer, items are notification objects which don't have 'pinned' attribute directly.
|
||||||
|
# But we check the status attached to the notification if it exists.
|
||||||
|
# However, notifications are strictly chronological usually. Pinned mentions don't exist?
|
||||||
|
# But let's stick to the safe ID extraction.
|
||||||
|
# The logic here is tricky because self.session.db stores notification objects, but sometimes just dicts?
|
||||||
|
# Let's assume they are objects with 'id' attribute.
|
||||||
|
# Notifications don't have 'pinned', so we just take the min ID.
|
||||||
|
# But wait, did I change this file previously to use min()? Yes.
|
||||||
|
# Is there any case where a notification ID is "pinned" (old)? No.
|
||||||
|
# So min() should be fine here. But for consistency with other buffers if any weird logic exists...
|
||||||
|
# Actually, let's keep min() as notifications don't support pinning.
|
||||||
|
|
||||||
|
max_id = min(item.id for item in self.session.db[self.name])
|
||||||
try:
|
try:
|
||||||
items = getattr(self.session.api, self.function)(max_id=max_id, limit=self.session.settings["general"]["max_posts_per_call"], types=["mention"], *self.args, **self.kwargs)
|
items = getattr(self.session.api, self.function)(max_id=max_id, limit=self.session.settings["general"]["max_posts_per_call"], types=["mention"], *self.args, **self.kwargs)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|||||||
@@ -33,10 +33,7 @@ class SearchBuffer(BaseBuffer):
|
|||||||
self.execution_time = current_time
|
self.execution_time = current_time
|
||||||
min_id = None
|
min_id = None
|
||||||
if self.name in self.session.db and len(self.session.db[self.name]) > 0:
|
if self.name in self.session.db and len(self.session.db[self.name]) > 0:
|
||||||
if self.session.settings["general"]["reverse_timelines"]:
|
min_id = max(item.id for item in self.session.db[self.name])
|
||||||
min_id = self.session.db[self.name][0].id
|
|
||||||
else:
|
|
||||||
min_id = self.session.db[self.name][-1].id
|
|
||||||
try:
|
try:
|
||||||
results = getattr(self.session.api, self.function)(min_id=min_id, **self.kwargs)
|
results = getattr(self.session.api, self.function)(min_id=min_id, **self.kwargs)
|
||||||
except Exception as mess:
|
except Exception as mess:
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ class Handler(object):
|
|||||||
compose=_("&Post"),
|
compose=_("&Post"),
|
||||||
reply=_("Re&ply"),
|
reply=_("Re&ply"),
|
||||||
share=_("&Boost"),
|
share=_("&Boost"),
|
||||||
|
quote=_("&Quote"),
|
||||||
fav=_("&Add to favorites"),
|
fav=_("&Add to favorites"),
|
||||||
unfav=_("Remove from favorites"),
|
unfav=_("Remove from favorites"),
|
||||||
view=_("&Show post"),
|
view=_("&Show post"),
|
||||||
@@ -92,6 +93,8 @@ class Handler(object):
|
|||||||
pub.sendMessage("createBuffer", buffer_type="UserBuffer", session_type=session.type, buffer_title=_("Blocked users"), parent_tab=root_position, start=False, kwargs=dict(parent=controller.view.nb, compose_func="compose_user", function="blocks", name="blocked", sessionObject=session, account=name))
|
pub.sendMessage("createBuffer", buffer_type="UserBuffer", session_type=session.type, buffer_title=_("Blocked users"), parent_tab=root_position, start=False, kwargs=dict(parent=controller.view.nb, compose_func="compose_user", function="blocks", name="blocked", sessionObject=session, account=name))
|
||||||
elif i == 'notifications':
|
elif i == 'notifications':
|
||||||
pub.sendMessage("createBuffer", buffer_type="NotificationsBuffer", session_type=session.type, buffer_title=_("Notifications"), parent_tab=root_position, start=False, kwargs=dict(parent=controller.view.nb, compose_func="compose_notification", function="notifications", name="notifications", sessionObject=session, account=name))
|
pub.sendMessage("createBuffer", buffer_type="NotificationsBuffer", session_type=session.type, buffer_title=_("Notifications"), parent_tab=root_position, start=False, kwargs=dict(parent=controller.view.nb, compose_func="compose_notification", function="notifications", name="notifications", sessionObject=session, account=name))
|
||||||
|
elif i == 'announcements':
|
||||||
|
pub.sendMessage("createBuffer", buffer_type="AnnouncementsBuffer", session_type=session.type, buffer_title=_("Announcements"), parent_tab=root_position, start=False, kwargs=dict(parent=controller.view.nb, function="announcements", name="announcements", sessionObject=session, account=name, sound="new_event.ogg"))
|
||||||
pub.sendMessage("createBuffer", buffer_type="EmptyBuffer", session_type="base", buffer_title=_("Timelines"), parent_tab=root_position, start=False, kwargs=dict(parent=controller.view.nb, name="timelines", account=name))
|
pub.sendMessage("createBuffer", buffer_type="EmptyBuffer", session_type="base", buffer_title=_("Timelines"), parent_tab=root_position, start=False, kwargs=dict(parent=controller.view.nb, name="timelines", account=name))
|
||||||
timelines_position =controller.view.search("timelines", name)
|
timelines_position =controller.view.search("timelines", name)
|
||||||
for i in session.settings["other_buffers"]["timelines"]:
|
for i in session.settings["other_buffers"]["timelines"]:
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import languageHandler
|
|||||||
from twitter_text import parse_tweet, config
|
from twitter_text import parse_tweet, config
|
||||||
from mastodon import MastodonError
|
from mastodon import MastodonError
|
||||||
from controller import messages
|
from controller import messages
|
||||||
from sessions.mastodon import templates
|
from sessions.mastodon import templates, utils
|
||||||
from wxUI.dialogs.mastodon import postDialogs
|
from wxUI.dialogs.mastodon import postDialogs
|
||||||
from extra.autocompletionUsers import completion
|
from extra.autocompletionUsers import completion
|
||||||
from . import userList
|
from . import userList
|
||||||
@@ -282,10 +282,7 @@ class editPost(post):
|
|||||||
# Extract text from post
|
# Extract text from post
|
||||||
if item.reblog != None:
|
if item.reblog != None:
|
||||||
item = item.reblog
|
item = item.reblog
|
||||||
text = item.content
|
text = utils.html_filter(item.content)
|
||||||
# Remove HTML tags from content
|
|
||||||
import re
|
|
||||||
text = re.sub('<[^<]+?>', '', text)
|
|
||||||
# Initialize parent class
|
# Initialize parent class
|
||||||
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
|
||||||
|
|||||||
@@ -52,10 +52,12 @@ class accountSettingsController(globalSettingsController):
|
|||||||
post_template = self.config["templates"]["post"]
|
post_template = self.config["templates"]["post"]
|
||||||
conversation_template = self.config["templates"]["conversation"]
|
conversation_template = self.config["templates"]["conversation"]
|
||||||
person_template = self.config["templates"]["person"]
|
person_template = self.config["templates"]["person"]
|
||||||
self.dialog.create_templates(post_template=post_template, conversation_template=conversation_template, person_template=person_template)
|
announcement_template = self.config.get("templates", {}).get("announcement", "$text. Published $published_at. $read")
|
||||||
|
self.dialog.create_templates(post_template=post_template, conversation_template=conversation_template, person_template=person_template, announcement_template=announcement_template)
|
||||||
widgetUtils.connect_event(self.dialog.templates.post, widgetUtils.BUTTON_PRESSED, self.edit_post_template)
|
widgetUtils.connect_event(self.dialog.templates.post, widgetUtils.BUTTON_PRESSED, self.edit_post_template)
|
||||||
widgetUtils.connect_event(self.dialog.templates.conversation, widgetUtils.BUTTON_PRESSED, self.edit_conversation_template)
|
widgetUtils.connect_event(self.dialog.templates.conversation, widgetUtils.BUTTON_PRESSED, self.edit_conversation_template)
|
||||||
widgetUtils.connect_event(self.dialog.templates.person, widgetUtils.BUTTON_PRESSED, self.edit_person_template)
|
widgetUtils.connect_event(self.dialog.templates.person, widgetUtils.BUTTON_PRESSED, self.edit_person_template)
|
||||||
|
widgetUtils.connect_event(self.dialog.templates.announcement, widgetUtils.BUTTON_PRESSED, self.edit_announcement_template)
|
||||||
self.dialog.create_other_buffers()
|
self.dialog.create_other_buffers()
|
||||||
buffer_values = self.get_buffers_list()
|
buffer_values = self.get_buffers_list()
|
||||||
self.dialog.buffers.insert_buffers(buffer_values)
|
self.dialog.buffers.insert_buffers(buffer_values)
|
||||||
@@ -109,6 +111,15 @@ class accountSettingsController(globalSettingsController):
|
|||||||
self.config.write()
|
self.config.write()
|
||||||
self.dialog.templates.person.SetLabel(_("Edit template for persons. Current template: {}").format(result))
|
self.dialog.templates.person.SetLabel(_("Edit template for persons. Current template: {}").format(result))
|
||||||
|
|
||||||
|
def edit_announcement_template(self, *args, **kwargs):
|
||||||
|
template = self.config.get("templates", {}).get("announcement", "$text. Published $published_at. $read")
|
||||||
|
control = EditTemplate(template=template, type="announcement")
|
||||||
|
result = control.run_dialog()
|
||||||
|
if result != "": # Template has been saved.
|
||||||
|
self.config["templates"]["announcement"] = result
|
||||||
|
self.config.write()
|
||||||
|
self.dialog.templates.announcement.SetLabel(_("Edit template for announcements. Current template: {}").format(result))
|
||||||
|
|
||||||
def save_configuration(self):
|
def save_configuration(self):
|
||||||
if self.config["general"]["relative_times"] != self.dialog.get_value("general", "relative_time"):
|
if self.config["general"]["relative_times"] != self.dialog.get_value("general", "relative_time"):
|
||||||
self.needs_restart = True
|
self.needs_restart = True
|
||||||
@@ -204,6 +215,7 @@ class accountSettingsController(globalSettingsController):
|
|||||||
all_buffers['blocked']=_("Blocked users")
|
all_buffers['blocked']=_("Blocked users")
|
||||||
all_buffers['muted']=_("Muted users")
|
all_buffers['muted']=_("Muted users")
|
||||||
all_buffers['notifications']=_("Notifications")
|
all_buffers['notifications']=_("Notifications")
|
||||||
|
all_buffers['announcements']=_("Announcements")
|
||||||
list_buffers = []
|
list_buffers = []
|
||||||
hidden_buffers=[]
|
hidden_buffers=[]
|
||||||
all_buffers_keys = list(all_buffers.keys())
|
all_buffers_keys = list(all_buffers.keys())
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
import re
|
import re
|
||||||
import wx
|
import wx
|
||||||
from typing import List
|
from typing import List
|
||||||
from sessions.mastodon.templates import post_variables, conversation_variables, person_variables
|
from sessions.mastodon.templates import post_variables, conversation_variables, person_variables, announcement_variables
|
||||||
from wxUI.dialogs import templateDialogs
|
from wxUI.dialogs import templateDialogs
|
||||||
|
|
||||||
class EditTemplate(object):
|
class EditTemplate(object):
|
||||||
@@ -13,6 +13,8 @@ class EditTemplate(object):
|
|||||||
self.variables = post_variables
|
self.variables = post_variables
|
||||||
elif type == "conversation":
|
elif type == "conversation":
|
||||||
self.variables = conversation_variables
|
self.variables = conversation_variables
|
||||||
|
elif type == "announcement":
|
||||||
|
self.variables = announcement_variables
|
||||||
else:
|
else:
|
||||||
self.variables = person_variables
|
self.variables = person_variables
|
||||||
self.template: str = template
|
self.template: str = template
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ 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")
|
||||||
@@ -38,3 +37,12 @@ ocr_image = string(default="win+alt+o")
|
|||||||
open_in_browser = string(default="alt+control+win+return")
|
open_in_browser = string(default="alt+control+win+return")
|
||||||
add_alias=string(default="")
|
add_alias=string(default="")
|
||||||
vote=string(default="alt+win+shift+v")
|
vote=string(default="alt+win+shift+v")
|
||||||
|
edit_post=string(default="")
|
||||||
|
open_favs_timeline=string(default="")
|
||||||
|
community_timeline=string(default="")
|
||||||
|
seekLeft=string(default="")
|
||||||
|
seekRight=string(default="")
|
||||||
|
manage_aliases=string(default="")
|
||||||
|
create_filter=string(default="")
|
||||||
|
manage_filters=string(default="")
|
||||||
|
manage_accounts=string(default="")
|
||||||
@@ -33,7 +33,6 @@ 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")
|
||||||
@@ -57,3 +56,12 @@ ocr_image = string(default="win+alt+o")
|
|||||||
open_in_browser = string(default="alt+control+win+return")
|
open_in_browser = string(default="alt+control+win+return")
|
||||||
add_alias=string(default="")
|
add_alias=string(default="")
|
||||||
vote=string(default="alt+win+shift+v")
|
vote=string(default="alt+win+shift+v")
|
||||||
|
edit_post=string(default="")
|
||||||
|
open_favs_timeline=string(default="")
|
||||||
|
community_timeline=string(default="")
|
||||||
|
seekLeft=string(default="")
|
||||||
|
seekRight=string(default="")
|
||||||
|
manage_aliases=string(default="")
|
||||||
|
create_filter=string(default="")
|
||||||
|
manage_filters=string(default="")
|
||||||
|
manage_accounts=string(default="")
|
||||||
@@ -33,7 +33,6 @@ 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")
|
||||||
@@ -60,3 +59,12 @@ add_alias=string(default="")
|
|||||||
mute_conversation=string(default="control+alt+win+back")
|
mute_conversation=string(default="control+alt+win+back")
|
||||||
find = string(default="control+win+{")
|
find = string(default="control+win+{")
|
||||||
vote=string(default="alt+win+shift+v")
|
vote=string(default="alt+win+shift+v")
|
||||||
|
edit_post=string(default="")
|
||||||
|
open_favs_timeline=string(default="")
|
||||||
|
community_timeline=string(default="")
|
||||||
|
seekLeft=string(default="")
|
||||||
|
seekRight=string(default="")
|
||||||
|
manage_aliases=string(default="")
|
||||||
|
create_filter=string(default="")
|
||||||
|
manage_filters=string(default="")
|
||||||
|
manage_accounts=string(default="")
|
||||||
@@ -33,7 +33,6 @@ 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")
|
||||||
@@ -60,3 +59,12 @@ add_alias=string(default="")
|
|||||||
mute_conversation=string(default="control+alt+win+back")
|
mute_conversation=string(default="control+alt+win+back")
|
||||||
find = string(default="control+win+{")
|
find = string(default="control+win+{")
|
||||||
vote=string(default="alt+win+shift+v")
|
vote=string(default="alt+win+shift+v")
|
||||||
|
edit_post=string(default="")
|
||||||
|
open_favs_timeline=string(default="")
|
||||||
|
community_timeline=string(default="")
|
||||||
|
seekLeft=string(default="")
|
||||||
|
seekRight=string(default="")
|
||||||
|
manage_aliases=string(default="")
|
||||||
|
create_filter=string(default="")
|
||||||
|
manage_filters=string(default="")
|
||||||
|
manage_accounts=string(default="")
|
||||||
@@ -59,3 +59,12 @@ open_in_browser = string(default="alt+control+win+return")
|
|||||||
add_alias=string(default="")
|
add_alias=string(default="")
|
||||||
mute_conversation=string(default="alt+win+shift+delete")
|
mute_conversation=string(default="alt+win+shift+delete")
|
||||||
vote=string(default="alt+win+shift+v")
|
vote=string(default="alt+win+shift+v")
|
||||||
|
edit_post=string(default="")
|
||||||
|
open_favs_timeline=string(default="")
|
||||||
|
community_timeline=string(default="")
|
||||||
|
seekLeft=string(default="")
|
||||||
|
seekRight=string(default="")
|
||||||
|
manage_aliases=string(default="")
|
||||||
|
create_filter=string(default="")
|
||||||
|
manage_filters=string(default="")
|
||||||
|
manage_accounts=string(default="")
|
||||||
@@ -34,7 +34,6 @@ 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")
|
||||||
@@ -61,3 +60,12 @@ open_in_browser = string(default="alt+control+win+return")
|
|||||||
add_alias=string(default="")
|
add_alias=string(default="")
|
||||||
mute_conversation=string(default="alt+win+shift+delete")
|
mute_conversation=string(default="alt+win+shift+delete")
|
||||||
vote=string(default="alt+win+shift+v")
|
vote=string(default="alt+win+shift+v")
|
||||||
|
edit_post=string(default="")
|
||||||
|
open_favs_timeline=string(default="")
|
||||||
|
community_timeline=string(default="")
|
||||||
|
seekLeft=string(default="")
|
||||||
|
seekRight=string(default="")
|
||||||
|
manage_aliases=string(default="")
|
||||||
|
create_filter=string(default="")
|
||||||
|
manage_filters=string(default="")
|
||||||
|
manage_accounts=string(default="")
|
||||||
@@ -29,7 +29,7 @@ actions = {
|
|||||||
"go_end": _(u"Jump to the last element of the current buffer"),
|
"go_end": _(u"Jump to the last element of the current buffer"),
|
||||||
"go_page_up": _(u"Jump 20 elements up in the current buffer"),
|
"go_page_up": _(u"Jump 20 elements up in the current buffer"),
|
||||||
"go_page_down": _(u"Jump 20 elements down in the current buffer"),
|
"go_page_down": _(u"Jump 20 elements down in the current buffer"),
|
||||||
# "update_profile": _(u"Edit profile"),
|
"update_profile": _(u"Edit profile"),
|
||||||
"delete": _("Delete post"),
|
"delete": _("Delete post"),
|
||||||
"clear_buffer": _(u"Empty the current buffer"),
|
"clear_buffer": _(u"Empty the current buffer"),
|
||||||
"repeat_item": _(u"Repeat last item"),
|
"repeat_item": _(u"Repeat last item"),
|
||||||
@@ -55,4 +55,14 @@ actions = {
|
|||||||
"ocr_image": _(u"Extracts the text from a picture and displays the result in a dialog."),
|
"ocr_image": _(u"Extracts the text from a picture and displays the result in a dialog."),
|
||||||
"add_alias": _("Adds an alias to an user"),
|
"add_alias": _("Adds an alias to an user"),
|
||||||
"mute_conversation": _("Mute/Unmute conversation"),
|
"mute_conversation": _("Mute/Unmute conversation"),
|
||||||
|
"edit_post": _(u"Edit the selected post"),
|
||||||
|
"vote": _(u"Vote in the selected poll"),
|
||||||
|
"open_favs_timeline": _(u"Open favorites timeline"),
|
||||||
|
"community_timeline": _(u"Open local/federated timeline"),
|
||||||
|
"seekLeft": _(u"Seek media backward"),
|
||||||
|
"seekRight": _(u"Seek media forward"),
|
||||||
|
"manage_aliases": _(u"Manage user aliases"),
|
||||||
|
"create_filter": _(u"Create a new filter"),
|
||||||
|
"manage_filters": _(u"Manage filters"),
|
||||||
|
"manage_accounts": _(u"Manage accounts"),
|
||||||
}
|
}
|
||||||
@@ -14,7 +14,7 @@ persist_size = integer(default=0)
|
|||||||
load_cache_in_memory=boolean(default=True)
|
load_cache_in_memory=boolean(default=True)
|
||||||
show_screen_names = boolean(default=False)
|
show_screen_names = boolean(default=False)
|
||||||
hide_emojis = boolean(default=False)
|
hide_emojis = boolean(default=False)
|
||||||
buffer_order = list(default=list('home', 'local', 'mentions', 'direct_messages', 'sent', 'favorites', 'bookmarks', 'followers', 'following', 'blocked', 'muted', 'notifications'))
|
buffer_order = list(default=list('home', 'local', 'mentions', 'direct_messages', 'sent', 'favorites', 'bookmarks', 'followers', 'following', 'blocked', 'muted', 'notifications', 'announcements'))
|
||||||
boost_mode = string(default="ask")
|
boost_mode = string(default="ask")
|
||||||
disable_streaming = boolean(default=False)
|
disable_streaming = boolean(default=False)
|
||||||
|
|
||||||
@@ -54,6 +54,7 @@ post = string(default="$display_name, $safe_text $image_descriptions $date. $vis
|
|||||||
person = string(default="$display_name (@$screen_name). $followers followers, $following following, $posts posts. Joined $created_at.")
|
person = string(default="$display_name (@$screen_name). $followers followers, $following following, $posts posts. Joined $created_at.")
|
||||||
conversation = string(default="Conversation with $users. Last message: $last_post")
|
conversation = string(default="Conversation with $users. Last message: $last_post")
|
||||||
notification = string(default="$display_name $text, $date")
|
notification = string(default="$display_name $text, $date")
|
||||||
|
announcement = string(default="$text. Published $published_at. $read")
|
||||||
|
|
||||||
[filters]
|
[filters]
|
||||||
|
|
||||||
|
|||||||
@@ -85,3 +85,9 @@ def compose_notification(notification, db, settings, relative_times, show_screen
|
|||||||
if filtered != None:
|
if filtered != None:
|
||||||
text = _("hidden by filter {}").format(filtered)
|
text = _("hidden by filter {}").format(filtered)
|
||||||
return [user, text, ts]
|
return [user, text, ts]
|
||||||
|
|
||||||
|
def compose_announcement(announcement, db, settings, relative_times, show_screen_names, safe=False):
|
||||||
|
# Use the default template or a configured one if available
|
||||||
|
template = settings.get("templates", {}).get("announcement", templates.announcement_default_template)
|
||||||
|
text = templates.render_announcement(announcement, template, settings, relative_times, db["utc_offset"])
|
||||||
|
return [text]
|
||||||
@@ -217,38 +217,69 @@ class Session(base.baseSession):
|
|||||||
self.sound.play(_sound)
|
self.sound.play(_sound)
|
||||||
return val
|
return val
|
||||||
|
|
||||||
def send_post(self, reply_to=None, visibility=None, language=None, posts=[]):
|
def _send_quote_post(self, text, quote_id, visibility, sensitive, spoiler_text, language, scheduled_at, in_reply_to_id=None, media_ids=[], poll=None):
|
||||||
|
"""Internal helper to send a quote post using direct API call."""
|
||||||
|
params = {
|
||||||
|
'status': text,
|
||||||
|
'visibility': visibility,
|
||||||
|
'quoted_status_id': quote_id,
|
||||||
|
}
|
||||||
|
if in_reply_to_id:
|
||||||
|
params['in_reply_to_id'] = in_reply_to_id
|
||||||
|
if sensitive:
|
||||||
|
params['sensitive'] = sensitive
|
||||||
|
if spoiler_text:
|
||||||
|
params['spoiler_text'] = spoiler_text
|
||||||
|
if language:
|
||||||
|
params['language'] = language
|
||||||
|
if scheduled_at:
|
||||||
|
if hasattr(scheduled_at, 'isoformat'):
|
||||||
|
params['scheduled_at'] = scheduled_at.isoformat()
|
||||||
|
else:
|
||||||
|
params['scheduled_at'] = scheduled_at
|
||||||
|
if media_ids:
|
||||||
|
params['media_ids'] = media_ids
|
||||||
|
if poll:
|
||||||
|
params['poll'] = poll
|
||||||
|
|
||||||
|
# Use the internal API request method directly
|
||||||
|
return self.api._Mastodon__api_request('POST', '/api/v1/statuses', params)
|
||||||
|
|
||||||
|
def send_post(self, reply_to=None, quote_id=None, visibility=None, language=None, posts=[]):
|
||||||
""" Convenience function to send a thread. """
|
""" Convenience function to send a thread. """
|
||||||
in_reply_to_id = reply_to
|
in_reply_to_id = reply_to
|
||||||
for obj in posts:
|
for obj in posts:
|
||||||
text = obj.get("text")
|
text = obj.get("text")
|
||||||
scheduled_at = obj.get("scheduled_at")
|
scheduled_at = obj.get("scheduled_at")
|
||||||
if len(obj["attachments"]) == 0:
|
|
||||||
|
# Prepare media and polls first as they are needed for both standard and quote posts
|
||||||
|
media_ids = []
|
||||||
|
poll = None
|
||||||
|
if len(obj["attachments"]) > 0:
|
||||||
try:
|
try:
|
||||||
item = self.api_call(call_name="status_post", status=text, _sound="tweet_send.ogg", in_reply_to_id=in_reply_to_id, visibility=visibility, sensitive=obj["sensitive"], spoiler_text=obj["spoiler_text"], language=language, scheduled_at=scheduled_at)
|
|
||||||
# If it fails, let's basically send an event with all passed info so we will catch it later.
|
|
||||||
except Exception as e:
|
|
||||||
pub.sendMessage("mastodon.error_post", name=self.get_name(), reply_to=reply_to, visibility=visibility, posts=posts, lang=language)
|
|
||||||
return
|
|
||||||
if item != None:
|
|
||||||
in_reply_to_id = item["id"]
|
|
||||||
else:
|
|
||||||
media_ids = []
|
|
||||||
try:
|
|
||||||
poll = None
|
|
||||||
if len(obj["attachments"]) == 1 and obj["attachments"][0]["type"] == "poll":
|
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"])
|
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"])
|
||||||
else:
|
else:
|
||||||
for i in obj["attachments"]:
|
for i in obj["attachments"]:
|
||||||
media = self.api_call("media_post", media_file=i["file"], description=i["description"], synchronous=True)
|
media = self.api_call("media_post", media_file=i["file"], description=i["description"], synchronous=True)
|
||||||
media_ids.append(media.id)
|
media_ids.append(media.id)
|
||||||
item = self.api_call(call_name="status_post", status=text, _sound="tweet_send.ogg", in_reply_to_id=in_reply_to_id, media_ids=media_ids, visibility=visibility, poll=poll, sensitive=obj["sensitive"], spoiler_text=obj["spoiler_text"], language=language, scheduled_at=scheduled_at)
|
|
||||||
if item != None:
|
|
||||||
in_reply_to_id = item["id"]
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
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, language=language)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
if quote_id:
|
||||||
|
item = self._send_quote_post(text, quote_id, visibility, obj["sensitive"], obj["spoiler_text"], language, scheduled_at, in_reply_to_id, media_ids, poll)
|
||||||
|
self.sound.play("tweet_send.ogg")
|
||||||
|
else:
|
||||||
|
item = self.api_call(call_name="status_post", status=text, _sound="tweet_send.ogg", in_reply_to_id=in_reply_to_id, media_ids=media_ids, visibility=visibility, poll=poll, sensitive=obj["sensitive"], spoiler_text=obj["spoiler_text"], language=language, scheduled_at=scheduled_at)
|
||||||
|
|
||||||
|
if item != None:
|
||||||
|
in_reply_to_id = item["id"]
|
||||||
|
except Exception as e:
|
||||||
|
pub.sendMessage("mastodon.error_post", name=self.get_name(), reply_to=reply_to, visibility=visibility, posts=posts, language=language)
|
||||||
|
return
|
||||||
|
|
||||||
def edit_post(self, post_id, posts=[]):
|
def edit_post(self, post_id, posts=[]):
|
||||||
""" Convenience function to edit a post. Only the first item in posts list is used as threads cannot be edited.
|
""" Convenience function to edit a post. Only the first item in posts list is used as threads cannot be edited.
|
||||||
|
|
||||||
|
|||||||
@@ -13,12 +13,14 @@ post_variables = ["date", "display_name", "screen_name", "source", "lang", "safe
|
|||||||
person_variables = ["display_name", "screen_name", "description", "followers", "following", "favorites", "posts", "created_at"]
|
person_variables = ["display_name", "screen_name", "description", "followers", "following", "favorites", "posts", "created_at"]
|
||||||
conversation_variables = ["users", "last_post"]
|
conversation_variables = ["users", "last_post"]
|
||||||
notification_variables = ["display_name", "screen_name", "text", "date"]
|
notification_variables = ["display_name", "screen_name", "text", "date"]
|
||||||
|
announcement_variables = ["text", "published_at", "updated_at", "starts_at", "ends_at", "read"]
|
||||||
|
|
||||||
# Default, translatable templates.
|
# Default, translatable templates.
|
||||||
post_default_template = _("$display_name, $text $image_descriptions $date. $source")
|
post_default_template = _("$display_name, $text $image_descriptions $date. $source")
|
||||||
dm_sent_default_template = _("Dm to $recipient_display_name, $text $date")
|
dm_sent_default_template = _("Dm to $recipient_display_name, $text $date")
|
||||||
person_default_template = _("$display_name (@$screen_name). $followers followers, $following following, $posts posts. Joined $created_at.")
|
person_default_template = _("$display_name (@$screen_name). $followers followers, $following following, $posts posts. Joined $created_at.")
|
||||||
notification_default_template = _("$display_name $text, $date")
|
notification_default_template = _("$display_name $text, $date")
|
||||||
|
announcement_default_template = _("$text. Published $published_at. $read")
|
||||||
|
|
||||||
def process_date(field, relative_times=True, offset_hours=0):
|
def process_date(field, relative_times=True, offset_hours=0):
|
||||||
original_date = arrow.get(field)
|
original_date = arrow.get(field)
|
||||||
@@ -185,3 +187,23 @@ def render_notification(notification, template, post_template, settings, relativ
|
|||||||
result = Template(_(template)).safe_substitute(**available_data)
|
result = Template(_(template)).safe_substitute(**available_data)
|
||||||
result = result.replace(" . ", "")
|
result = result.replace(" . ", "")
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
def render_announcement(announcement, template, settings, relative_times=False, offset_hours=0):
|
||||||
|
""" Renders any given announcement according to the passed template. """
|
||||||
|
global announcement_variables
|
||||||
|
available_data = dict()
|
||||||
|
# Process dates
|
||||||
|
for date_field in ["published_at", "updated_at", "starts_at", "ends_at"]:
|
||||||
|
if hasattr(announcement, date_field) and getattr(announcement, date_field) is not None:
|
||||||
|
available_data[date_field] = process_date(getattr(announcement, date_field), relative_times, offset_hours)
|
||||||
|
else:
|
||||||
|
available_data[date_field] = ""
|
||||||
|
|
||||||
|
available_data["text"] = utils.html_filter(announcement.content)
|
||||||
|
if announcement.read:
|
||||||
|
available_data["read"] = _("Read")
|
||||||
|
else:
|
||||||
|
available_data["read"] = _("Unread")
|
||||||
|
|
||||||
|
result = Template(_(template)).safe_substitute(**available_data)
|
||||||
|
return result
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import sys
|
|||||||
import application
|
import application
|
||||||
import platform
|
import platform
|
||||||
import os
|
import os
|
||||||
from cx_Freeze import setup, Executable, winmsvcr
|
from cx_Freeze import setup, Executable
|
||||||
from requests import certs
|
from requests import certs
|
||||||
|
|
||||||
def get_architecture_files():
|
def get_architecture_files():
|
||||||
@@ -34,7 +34,7 @@ def find_accessible_output2_datafiles():
|
|||||||
|
|
||||||
base = None
|
base = None
|
||||||
if sys.platform == 'win32':
|
if sys.platform == 'win32':
|
||||||
base = 'Win32GUI'
|
base = 'GUI'
|
||||||
|
|
||||||
build_exe_options = dict(
|
build_exe_options = dict(
|
||||||
build_exe="dist",
|
build_exe="dist",
|
||||||
@@ -51,8 +51,6 @@ executables = [
|
|||||||
Executable('main.py', base=base, target_name="twblue")
|
Executable('main.py', base=base, target_name="twblue")
|
||||||
]
|
]
|
||||||
|
|
||||||
winmsvcr.FILES = ()
|
|
||||||
winmsvcr.FILES_TO_DUPLICATE = ()
|
|
||||||
setup(name=application.name,
|
setup(name=application.name,
|
||||||
version=application.version,
|
version=application.version,
|
||||||
description=application.description,
|
description=application.description,
|
||||||
|
|||||||
@@ -3,3 +3,4 @@ from .base import basePanel
|
|||||||
from .conversationList import conversationListPanel
|
from .conversationList import conversationListPanel
|
||||||
from .notifications import notificationsPanel
|
from .notifications import notificationsPanel
|
||||||
from .user import userPanel
|
from .user import userPanel
|
||||||
|
from .announcements import announcementsPanel
|
||||||
|
|||||||
36
srcantiguo/wxUI/buffers/mastodon/announcements.py
Normal file
36
srcantiguo/wxUI/buffers/mastodon/announcements.py
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import wx
|
||||||
|
from multiplatform_widgets import widgets
|
||||||
|
|
||||||
|
class announcementsPanel(wx.Panel):
|
||||||
|
|
||||||
|
def set_focus_function(self, f):
|
||||||
|
self.list.list.Bind(wx.EVT_LIST_ITEM_FOCUSED, f)
|
||||||
|
|
||||||
|
def create_list(self):
|
||||||
|
self.list = widgets.list(self, _("Announcement"), style=wx.LC_REPORT|wx.LC_SINGLE_SEL|wx.LC_VRULES)
|
||||||
|
self.list.set_windows_size(0, 800)
|
||||||
|
self.list.set_size()
|
||||||
|
|
||||||
|
def __init__(self, parent, name):
|
||||||
|
super(announcementsPanel, self).__init__(parent)
|
||||||
|
self.name = name
|
||||||
|
self.type = "baseBuffer"
|
||||||
|
self.sizer = wx.BoxSizer(wx.VERTICAL)
|
||||||
|
self.create_list()
|
||||||
|
self.dismiss = wx.Button(self, -1, _("Dismiss"))
|
||||||
|
btnSizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
|
btnSizer.Add(self.dismiss, 0, wx.ALL, 5)
|
||||||
|
self.sizer.Add(btnSizer, 0, wx.ALL, 5)
|
||||||
|
self.sizer.Add(self.list.list, 1, wx.ALL|wx.EXPAND, 5)
|
||||||
|
self.SetSizer(self.sizer)
|
||||||
|
self.SetClientSize(self.sizer.CalcMin())
|
||||||
|
|
||||||
|
def set_position(self, reversed=False):
|
||||||
|
if reversed == False:
|
||||||
|
self.list.select_item(self.list.get_count()-1)
|
||||||
|
else:
|
||||||
|
self.list.select_item(0)
|
||||||
|
|
||||||
|
def set_focus_in_list(self):
|
||||||
|
self.list.list.SetFocus()
|
||||||
@@ -47,7 +47,7 @@ class generalAccount(wx.Panel, baseDialog.BaseWXDialog):
|
|||||||
self.SetSizer(sizer)
|
self.SetSizer(sizer)
|
||||||
|
|
||||||
class templates(wx.Panel, baseDialog.BaseWXDialog):
|
class templates(wx.Panel, baseDialog.BaseWXDialog):
|
||||||
def __init__(self, parent, post_template, conversation_template, person_template):
|
def __init__(self, parent, post_template, conversation_template, person_template, announcement_template):
|
||||||
super(templates, self).__init__(parent)
|
super(templates, self).__init__(parent)
|
||||||
sizer = wx.BoxSizer(wx.VERTICAL)
|
sizer = wx.BoxSizer(wx.VERTICAL)
|
||||||
self.post = wx.Button(self, wx.ID_ANY, _("Edit template for &posts. Current template: {}").format(post_template))
|
self.post = wx.Button(self, wx.ID_ANY, _("Edit template for &posts. Current template: {}").format(post_template))
|
||||||
@@ -56,6 +56,8 @@ class templates(wx.Panel, baseDialog.BaseWXDialog):
|
|||||||
sizer.Add(self.conversation, 0, wx.ALL, 5)
|
sizer.Add(self.conversation, 0, wx.ALL, 5)
|
||||||
self.person = wx.Button(self, wx.ID_ANY, _("Edit template for p&ersons. Current template: {}").format(person_template))
|
self.person = wx.Button(self, wx.ID_ANY, _("Edit template for p&ersons. Current template: {}").format(person_template))
|
||||||
sizer.Add(self.person, 0, wx.ALL, 5)
|
sizer.Add(self.person, 0, wx.ALL, 5)
|
||||||
|
self.announcement = wx.Button(self, wx.ID_ANY, _("Edit template for &announcements. Current template: {}").format(announcement_template))
|
||||||
|
sizer.Add(self.announcement, 0, wx.ALL, 5)
|
||||||
self.SetSizer(sizer)
|
self.SetSizer(sizer)
|
||||||
|
|
||||||
class sound(wx.Panel):
|
class sound(wx.Panel):
|
||||||
@@ -152,8 +154,8 @@ class configurationDialog(baseDialog.BaseWXDialog):
|
|||||||
self.buffers = other_buffers(self.notebook)
|
self.buffers = other_buffers(self.notebook)
|
||||||
self.notebook.AddPage(self.buffers, _(u"Buffers"))
|
self.notebook.AddPage(self.buffers, _(u"Buffers"))
|
||||||
|
|
||||||
def create_templates(self, post_template, conversation_template, person_template):
|
def create_templates(self, post_template, conversation_template, person_template, announcement_template):
|
||||||
self.templates = templates(self.notebook, post_template=post_template, conversation_template=conversation_template, person_template=person_template)
|
self.templates = templates(self.notebook, post_template=post_template, conversation_template=conversation_template, person_template=person_template, announcement_template=announcement_template)
|
||||||
self.notebook.AddPage(self.templates, _("Templates"))
|
self.notebook.AddPage(self.templates, _("Templates"))
|
||||||
|
|
||||||
def create_sound(self, output_devices, input_devices, soundpacks):
|
def create_sound(self, output_devices, input_devices, soundpacks):
|
||||||
|
|||||||
@@ -2,11 +2,43 @@
|
|||||||
import wx
|
import wx
|
||||||
import application
|
import application
|
||||||
|
|
||||||
|
class BoostDialog(wx.Dialog):
|
||||||
|
def __init__(self):
|
||||||
|
super(BoostDialog, self).__init__(None, title=_("Boost"))
|
||||||
|
p = wx.Panel(self)
|
||||||
|
sizer = wx.BoxSizer(wx.VERTICAL)
|
||||||
|
lbl = wx.StaticText(p, wx.ID_ANY, _("What would you like to do with this post?"))
|
||||||
|
sizer.Add(lbl, 0, wx.ALL, 10)
|
||||||
|
|
||||||
|
btn_sizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
|
self.btn_boost = wx.Button(p, wx.ID_ANY, _("Boost"))
|
||||||
|
self.btn_quote = wx.Button(p, wx.ID_ANY, _("Quote"))
|
||||||
|
self.btn_cancel = wx.Button(p, wx.ID_CANCEL, _("Cancel"))
|
||||||
|
|
||||||
|
btn_sizer.Add(self.btn_boost, 0, wx.ALL, 5)
|
||||||
|
btn_sizer.Add(self.btn_quote, 0, wx.ALL, 5)
|
||||||
|
btn_sizer.Add(self.btn_cancel, 0, wx.ALL, 5)
|
||||||
|
|
||||||
|
sizer.Add(btn_sizer, 0, wx.ALIGN_CENTER)
|
||||||
|
p.SetSizer(sizer)
|
||||||
|
sizer.Fit(self)
|
||||||
|
|
||||||
|
self.btn_boost.Bind(wx.EVT_BUTTON, self.on_boost)
|
||||||
|
self.btn_quote.Bind(wx.EVT_BUTTON, self.on_quote)
|
||||||
|
self.result = 0
|
||||||
|
|
||||||
|
def on_boost(self, event):
|
||||||
|
self.result = 1
|
||||||
|
self.EndModal(wx.ID_OK)
|
||||||
|
|
||||||
|
def on_quote(self, event):
|
||||||
|
self.result = 2
|
||||||
|
self.EndModal(wx.ID_OK)
|
||||||
|
|
||||||
def boost_question():
|
def boost_question():
|
||||||
result = False
|
dlg = BoostDialog()
|
||||||
dlg = wx.MessageDialog(None, _("Would you like to share this post?"), _("Boost"), wx.YES_NO|wx.ICON_QUESTION)
|
dlg.ShowModal()
|
||||||
if dlg.ShowModal() == wx.ID_YES:
|
result = dlg.result
|
||||||
result = True
|
|
||||||
dlg.Destroy()
|
dlg.Destroy()
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ class base(wx.Menu):
|
|||||||
super(base, self).__init__()
|
super(base, self).__init__()
|
||||||
self.boost = wx.MenuItem(self, wx.ID_ANY, _("&Boost"))
|
self.boost = wx.MenuItem(self, wx.ID_ANY, _("&Boost"))
|
||||||
self.Append(self.boost)
|
self.Append(self.boost)
|
||||||
|
self.quote = wx.MenuItem(self, wx.ID_ANY, _("&Quote"))
|
||||||
|
self.Append(self.quote)
|
||||||
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.edit = wx.MenuItem(self, wx.ID_ANY, _(u"&Edit"))
|
||||||
@@ -38,6 +40,8 @@ class notification(wx.Menu):
|
|||||||
if item in valid_types:
|
if item in valid_types:
|
||||||
self.boost = wx.MenuItem(self, wx.ID_ANY, _("&Boost"))
|
self.boost = wx.MenuItem(self, wx.ID_ANY, _("&Boost"))
|
||||||
self.Append(self.boost)
|
self.Append(self.boost)
|
||||||
|
self.quote = wx.MenuItem(self, wx.ID_ANY, _("&Quote"))
|
||||||
|
self.Append(self.quote)
|
||||||
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.edit = wx.MenuItem(self, wx.ID_ANY, _(u"&Edit"))
|
||||||
|
|||||||
Reference in New Issue
Block a user