Commit f68fa1da authored by mathieui's avatar mathieui

Merge branch 'split-message-rendering' into 'master'

split message rendering

See merge request !48
parents d22b4b8c ca85411a
Pipeline #3064 pending with stages
......@@ -4,11 +4,12 @@ date has changed.
"""
import datetime
from gettext import gettext as _
from poezio import timed_events, tabs
from poezio.plugin import BasePlugin
import datetime
from poezio import tabs
from poezio import timed_events
from poezio.ui.types import InfoMessage
class Plugin(BasePlugin):
......@@ -30,7 +31,7 @@ class Plugin(BasePlugin):
for tab in self.core.tabs:
if isinstance(tab, tabs.ChatTab):
tab.add_message(msg)
tab.add_message(InfoMessage(msg))
self.core.refresh_window()
self.schedule_event()
......@@ -16,6 +16,7 @@ Usage
from poezio import tabs
from poezio.plugin import BasePlugin
from poezio.theming import get_theme
from poezio.ui.types import Message
class Plugin(BasePlugin):
......@@ -37,11 +38,13 @@ class Plugin(BasePlugin):
if not isinstance(tab, tabs.MucTab):
message['type'] = 'chat'
tab.add_message(
message['body'],
nickname=tab.core.own_nick,
nick_color=get_theme().COLOR_OWN_NICK,
identifier=message['id'],
jid=tab.core.xmpp.boundjid,
Message(
message['body'],
nickname=tab.core.own_nick,
nick_color=get_theme().COLOR_OWN_NICK,
identifier=message['id'],
jid=tab.core.xmpp.boundjid,
),
typ=1,
)
message.send()
......
......@@ -17,7 +17,8 @@ from datetime import datetime
from poezio.plugin import BasePlugin
from poezio import tabs
from poezio.text_buffer import Message, TextBuffer
from poezio.text_buffer import TextBuffer
from poezio.ui.types import InfoMessage
def add_line(
......@@ -26,18 +27,7 @@ def add_line(
datetime: Optional[datetime] = None,
) -> None:
"""Adds a textual entry in the TextBuffer"""
text_buffer.add_message(
text,
datetime, # Time
None, # Nickname
None, # Nick Color
False, # History
None, # User
False, # Highlight
None, # Identifier
None, # str_time
None, # Jid
)
text_buffer.add_message(InfoMessage(text, time=datetime))
class Plugin(BasePlugin):
......
......@@ -205,6 +205,7 @@ from poezio.tabs import StaticConversationTab, PrivateTab
from poezio.theming import get_theme, dump_tuple
from poezio.decorators import command_args_parser
from poezio.core.structs import Completion
from poezio.ui.types import InfoMessage, Message
POLICY_FLAGS = {
'ALLOW_V1': False,
......@@ -385,25 +386,30 @@ class PoezioContext(Context):
log.debug('OTR conversation with %s refreshed', self.peer)
if self.getCurrentTrust():
msg = OTR_REFRESH_TRUSTED % format_dict
tab.add_message(msg, typ=self.log)
tab.add_message(InfoMessage(msg), typ=self.log)
else:
msg = OTR_REFRESH_UNTRUSTED % format_dict
tab.add_message(msg, typ=self.log)
tab.add_message(InfoMessage(msg), typ=self.log)
hl(tab)
elif newstate == STATE_FINISHED or newstate == STATE_PLAINTEXT:
log.debug('OTR conversation with %s finished', self.peer)
if tab:
tab.add_message(OTR_END % format_dict, typ=self.log)
tab.add_message(InfoMessage(OTR_END % format_dict), typ=self.log)
hl(tab)
elif newstate == STATE_ENCRYPTED and tab:
if self.getCurrentTrust():
tab.add_message(OTR_START_TRUSTED % format_dict, typ=self.log)
tab.add_message(InfoMessage(OTR_START_TRUSTED % format_dict), typ=self.log)
else:
format_dict['our_fpr'] = self.user.getPrivkey()
format_dict['remote_fpr'] = self.getCurrentKey()
tab.add_message(OTR_TUTORIAL % format_dict, typ=0)
tab.add_message(
OTR_START_UNTRUSTED % format_dict, typ=self.log)
InfoMessage(OTR_TUTORIAL % format_dict),
typ=0
)
tab.add_message(
InfoMessage(OTR_START_UNTRUSTED % format_dict),
typ=self.log,
)
hl(tab)
log.debug('Set encryption state of %s to %s', self.peer,
......@@ -639,7 +645,7 @@ class Plugin(BasePlugin):
# Received an OTR error
proto_error = err.args[0].error # pylint: disable=no-member
format_dict['err'] = proto_error.decode('utf-8', errors='replace')
tab.add_message(OTR_ERROR % format_dict, typ=0)
tab.add_message(InfoMessage(OTR_ERROR % format_dict), typ=0)
del msg['body']
del msg['html']
hl(tab)
......@@ -649,7 +655,7 @@ class Plugin(BasePlugin):
# Encrypted message received, but unreadable as we do not have
# an OTR session in place.
text = MESSAGE_UNREADABLE % format_dict
tab.add_message(text, jid=msg['from'], typ=0)
tab.add_message(InfoMessage(text), typ=0)
hl(tab)
del msg['body']
del msg['html']
......@@ -658,7 +664,7 @@ class Plugin(BasePlugin):
except crypt.InvalidParameterError:
# Malformed OTR payload and stuff
text = MESSAGE_INVALID % format_dict
tab.add_message(text, jid=msg['from'], typ=0)
tab.add_message(InfoMessage(text), typ=0)
hl(tab)
del msg['body']
del msg['html']
......@@ -669,7 +675,7 @@ class Plugin(BasePlugin):
import traceback
exc = traceback.format_exc()
format_dict['exc'] = exc
tab.add_message(POTR_ERROR % format_dict, typ=0)
tab.add_message(InfoMessage(POTR_ERROR % format_dict), typ=0)
log.error('Unspecified error in the OTR plugin', exc_info=True)
return
# No error, proceed with the message
......@@ -688,10 +694,10 @@ class Plugin(BasePlugin):
abort = get_tlv(tlvs, potr.proto.SMPABORTTLV)
if abort:
ctx.reset_smp()
tab.add_message(SMP_ABORTED_PEER % format_dict, typ=0)
tab.add_message(InfoMessage(SMP_ABORTED_PEER % format_dict), typ=0)
elif ctx.in_smp and not ctx.smpIsValid():
ctx.reset_smp()
tab.add_message(SMP_ABORTED % format_dict, typ=0)
tab.add_message(InfoMessage(SMP_ABORTED % format_dict), typ=0)
elif smp1 or smp1q:
# Received an SMP request (with a question or not)
if smp1q:
......@@ -709,22 +715,22 @@ class Plugin(BasePlugin):
# we did not initiate it
ctx.smp_own = False
format_dict['q'] = question
tab.add_message(SMP_REQUESTED % format_dict, typ=0)
tab.add_message(InfoMessage(SMP_REQUESTED % format_dict), typ=0)
elif smp2:
# SMP reply received
if not ctx.in_smp:
ctx.reset_smp()
else:
tab.add_message(SMP_PROGRESS % format_dict, typ=0)
tab.add_message(InfoMessage(SMP_PROGRESS % format_dict), typ=0)
elif smp3 or smp4:
# Type 4 (SMP message 3) or 5 (SMP message 4) TLVs received
# in both cases it is the final message of the SMP exchange
if ctx.smpIsSuccess():
tab.add_message(SMP_SUCCESS % format_dict, typ=0)
tab.add_message(InfoMessage(SMP_SUCCESS % format_dict), typ=0)
if not ctx.getCurrentTrust():
tab.add_message(SMP_RECIPROCATE % format_dict, typ=0)
tab.add_message(InfoMessage(SMP_RECIPROCATE % format_dict), typ=0)
else:
tab.add_message(SMP_FAIL % format_dict, typ=0)
tab.add_message(InfoMessage(SMP_FAIL % format_dict), typ=0)
ctx.reset_smp()
hl(tab)
self.core.refresh_window()
......@@ -780,12 +786,15 @@ class Plugin(BasePlugin):
if decode_newlines:
body = body.replace('<br/>', '\n').replace('<br>', '\n')
tab.add_message(
body,
nickname=tab.nick,
jid=msg['from'],
forced_user=user,
Message(
body,
nickname=tab.nick,
jid=msg['from'],
user=user,
nick_color=nick_color
),
typ=ctx.log,
nick_color=nick_color)
)
hl(tab)
self.core.refresh_window()
del msg['body']
......@@ -826,19 +835,22 @@ class Plugin(BasePlugin):
tab.send_chat_state('inactive', always_send=True)
tab.add_message(
msg['body'],
nickname=self.core.own_nick or tab.own_nick,
nick_color=get_theme().COLOR_OWN_NICK,
identifier=msg['id'],
jid=self.core.xmpp.boundjid,
typ=ctx.log)
Message(
msg['body'],
nickname=self.core.own_nick or tab.own_nick,
nick_color=get_theme().COLOR_OWN_NICK,
identifier=msg['id'],
jid=self.core.xmpp.boundjid,
),
typ=ctx.log
)
# remove everything from the message so that it doesn’t get sent
del msg['body']
del msg['replace']
del msg['html']
elif is_relevant(tab) and ctx and ctx.getPolicy('REQUIRE_ENCRYPTION'):
warning_msg = MESSAGE_NOT_SENT % format_dict
tab.add_message(warning_msg, typ=0)
tab.add_message(InfoMessage(warning_msg), typ=0)
del msg['body']
del msg['replace']
del msg['html']
......@@ -856,7 +868,7 @@ class Plugin(BasePlugin):
('\n - /message %s' % jid) for jid in res)
format_dict['help'] = help_msg
warning_msg = INCOMPATIBLE_TAB % format_dict
tab.add_message(warning_msg, typ=0)
tab.add_message(InfoMessage(warning_msg), typ=0)
del msg['body']
del msg['replace']
del msg['html']
......@@ -900,22 +912,22 @@ class Plugin(BasePlugin):
self.otr_start(tab, name, format_dict)
elif action == 'ourfpr':
format_dict['fpr'] = self.account.getPrivkey()
tab.add_message(OTR_OWN_FPR % format_dict, typ=0)
tab.add_message(InfoMessage(OTR_OWN_FPR % format_dict), typ=0)
elif action == 'fpr':
if name in self.contexts:
ctx = self.contexts[name]
if ctx.getCurrentKey() is not None:
format_dict['fpr'] = ctx.getCurrentKey()
tab.add_message(OTR_REMOTE_FPR % format_dict, typ=0)
tab.add_message(InfoMessage(OTR_REMOTE_FPR % format_dict), typ=0)
else:
tab.add_message(OTR_NO_FPR % format_dict, typ=0)
tab.add_message(InfoMessage(OTR_NO_FPR % format_dict), typ=0)
elif action == 'drop':
# drop the privkey (and obviously, end the current conversations before that)
for context in self.contexts.values():
if context.state not in (STATE_FINISHED, STATE_PLAINTEXT):
context.disconnect()
self.account.drop_privkey()
tab.add_message(KEY_DROPPED % format_dict, typ=0)
tab.add_message(InfoMessage(KEY_DROPPED % format_dict), typ=0)
elif action == 'trust':
ctx = self.get_context(name)
key = ctx.getCurrentKey()
......@@ -927,7 +939,7 @@ class Plugin(BasePlugin):
format_dict['key'] = key
ctx.setTrust(fpr, 'verified')
self.account.saveTrusts()
tab.add_message(TRUST_ADDED % format_dict, typ=0)
tab.add_message(InfoMessage(TRUST_ADDED % format_dict), typ=0)
elif action == 'untrust':
ctx = self.get_context(name)
key = ctx.getCurrentKey()
......@@ -939,7 +951,7 @@ class Plugin(BasePlugin):
format_dict['key'] = key
ctx.setTrust(fpr, '')
self.account.saveTrusts()
tab.add_message(TRUST_REMOVED % format_dict, typ=0)
tab.add_message(InfoMessage(TRUST_REMOVED % format_dict), typ=0)
self.core.refresh_window()
def otr_start(self, tab, name, format_dict):
......@@ -954,7 +966,7 @@ class Plugin(BasePlugin):
if otr.state != STATE_ENCRYPTED:
format_dict['secs'] = secs
text = OTR_NOT_ENABLED % format_dict
tab.add_message(text, typ=0)
tab.add_message(InfoMessage(text), typ=0)
self.core.refresh_window()
if secs > 0:
......@@ -962,7 +974,7 @@ class Plugin(BasePlugin):
self.api.add_timed_event(event)
body = self.get_context(name).sendMessage(0, b'?OTRv?').decode()
self.core.xmpp.send_message(mto=name, mtype='chat', mbody=body)
tab.add_message(OTR_REQUEST % format_dict, typ=0)
tab.add_message(InfoMessage(OTR_REQUEST % format_dict), typ=0)
@staticmethod
def completion_otr(the_input):
......@@ -1012,13 +1024,13 @@ class Plugin(BasePlugin):
ctx.smpInit(secret, question)
else:
ctx.smpInit(secret)
tab.add_message(SMP_INITIATED % format_dict, typ=0)
tab.add_message(InfoMessage(SMP_INITIATED % format_dict), typ=0)
elif action == 'answer':
ctx.smpGotSecret(secret)
elif action == 'abort':
if ctx.in_smp:
ctx.smpAbort()
tab.add_message(SMP_ABORTED % format_dict, typ=0)
tab.add_message(InfoMessage(SMP_ABORTED % format_dict), typ=0)
self.core.refresh_window()
@staticmethod
......
......@@ -53,8 +53,15 @@ from poezio.core.completions import CompletionCore
from poezio.core.tabs import Tabs
from poezio.core.commands import CommandCore
from poezio.core.handlers import HandlerCore
from poezio.core.structs import POSSIBLE_SHOW, DEPRECATED_ERRORS, \
ERROR_AND_STATUS_CODES, Command, Status
from poezio.core.structs import (
Command,
Status,
DEPRECATED_ERRORS,
ERROR_AND_STATUS_CODES,
POSSIBLE_SHOW,
)
from poezio.ui.types import Message, InfoMessage
log = logging.getLogger(__name__)
......@@ -1317,7 +1324,7 @@ class Core:
"""
tab = self.tabs.by_name_and_class(jid, tabs.ConversationTab)
if tab is not None:
tab.add_message(msg, typ=2)
tab.add_message(InfoMessage(msg), typ=2)
if self.tabs.current_tab is tab:
self.refresh_window()
......@@ -1349,7 +1356,12 @@ class Core:
colors = get_theme().INFO_COLORS
color = colors.get(typ.lower(), colors.get('default', None))
nb_lines = self.information_buffer.add_message(
msg, nickname=typ, nick_color=color)
Message(
txt=msg,
nickname=typ,
nick_color=color
)
)
popup_on = config.get('information_buffer_popup_on').split()
if isinstance(self.tabs.current_tab, tabs.RosterInfoTab):
self.refresh_window()
......@@ -1579,17 +1591,6 @@ class Core:
self.tab_win.resize(1, tabs.Tab.width, tabs.Tab.height - 2, 0)
self.left_tab_win = None
def add_message_to_text_buffer(self, buff, txt, nickname=None):
"""
Add the message to the room if possible, else, add it to the Info window
(in the Info tab of the info window in the RosterTab)
"""
if not buff:
self.information('Trying to add a message in no room: %s' % txt,
'Error')
return
buff.add_message(txt, nickname=nickname)
def full_screen_redraw(self):
"""
Completely erase and redraw the screen
......@@ -2061,15 +2062,18 @@ class Core:
return
error_message = self.get_error_message(error)
tab.add_message(
error_message,
highlight=True,
nickname='Error',
nick_color=get_theme().COLOR_ERROR_MSG,
typ=2)
Message(
error_message,
highlight=True,
nickname='Error',
nick_color=get_theme().COLOR_ERROR_MSG,
),
typ=2,
)
code = error['error']['code']
if code == '401':
msg = 'To provide a password in order to join the room, type "/join / password" (replace "password" by the real password)'
tab.add_message(msg, typ=2)
tab.add_message(InfoMessage(msg), typ=2)
if code == '409':
if config.get('alternative_nickname') != '':
if not tab.joined:
......@@ -2078,8 +2082,12 @@ class Core:
else:
if not tab.joined:
tab.add_message(
'You can join the room with an other nick, by typing "/join /other_nick"',
typ=2)
InfoMessage(
'You can join the room with another nick, '
'by typing "/join /other_nick"'
),
typ=2,
)
self.refresh_window()
......
<
......@@ -39,6 +39,7 @@ from poezio.logger import logger
from poezio.roster import roster
from poezio.text_buffer import CorrectionError, AckError
from poezio.theming import dump_tuple, get_theme
from poezio.ui.types import XMLLog, Message as PMessage, BaseMessage, InfoMessage
from poezio.core.commands import dumb_callback
......@@ -326,7 +327,7 @@ class HandlerCore:
error = '\x19%s}%s\x19o' % (dump_tuple(get_theme().COLOR_CHAR_NACK),
error_msg)
if not tab.nack_message('\n' + error, message['id'], message['to']):
tab.add_message(error, typ=0)
tab.add_message(InfoMessage(error), typ=0)
self.core.refresh_window()
def on_normal_message(self, message):
......@@ -421,14 +422,17 @@ class HandlerCore:
if not try_modify():
conversation.add_message(
body,
date,
nickname=remote_nick,
nick_color=color,
history=delayed,
identifier=message['id'],
jid=jid,
typ=1)
PMessage(
txt=body,
time=date,
nickname=remote_nick,
nick_color=color,
history=delayed,
identifier=message['id'],
jid=jid,
),
typ=1,
)
if not own and 'private' in config.get('beep_on').split():
if not config.get_by_tabname('disable_beep', conv_jid.bare):
......@@ -750,6 +754,11 @@ class HandlerCore:
old_state = tab.state
delayed, date = common.find_delayed_tag(message)
history = (tab.last_message_was_history is None and delayed) or \
(tab.last_message_was_history and delayed)
tab.last_message_was_history = history
replaced = False
if message.xml.find('{urn:xmpp:message-correct:0}replace') is not None:
replaced_id = message['replace']['id']
......@@ -762,6 +771,7 @@ class HandlerCore:
replaced_id,
message['id'],
time=delayed_date,
delayed=delayed,
nickname=nick_from,
user=user):
self.core.events.trigger('highlight', message, tab)
......@@ -769,12 +779,16 @@ class HandlerCore:
except CorrectionError:
log.debug('Unable to correct a message', exc_info=True)
if not replaced and tab.add_message(
body,
date,
nick_from,
history=delayed,
identifier=message['id'],
jid=message['from'],
PMessage(
txt=body,
time=date,
nickname=nick_from,
history=history,
delayed=delayed,
identifier=message['id'],
jid=message['from'],
user=user,
),
typ=1):
self.core.events.trigger('highlight', message, tab)
......@@ -862,14 +876,16 @@ class HandlerCore:
log.debug('Unable to correct a message', exc_info=True)
if not replaced:
tab.add_message(
body,
time=None,
nickname=sender_nick,
nick_color=get_theme().COLOR_OWN_NICK if sent else None,
forced_user=user,
identifier=message['id'],
jid=message['from'],
typ=1)
PMessage(
txt=body,
nickname=sender_nick,
nick_color=get_theme().COLOR_OWN_NICK if sent else None,
user=user,
identifier=message['id'],
jid=message['from'],
),
typ=1,
)
if sent:
tab.set_last_sent_message(message, correct=replaced)
else:
......@@ -1361,36 +1377,52 @@ class HandlerCore:
if show_unavailable or hide_unavailable or non_priv or logging_off\
or non_anon or semi_anon or full_anon:
tab.add_message(
'\x19%(info_col)s}Info: A configuration change not privacy-related occurred.' % info_col,
InfoMessage(
'Info: A configuration change not privacy-related occurred.'
),
typ=2)
modif = True
if show_unavailable:
tab.add_message(
'\x19%(info_col)s}Info: The unavailable members are now shown.' % info_col,
InfoMessage(
'Info: The unavailable members are now shown.'
),
typ=2)
elif hide_unavailable:
tab.add_message(
'\x19%(info_col)s}Info: The unavailable members are now hidden.' % info_col,
InfoMessage(
'Info: The unavailable members are now hidden.',
),
typ=2)
if non_anon:
tab.add_message(
'\x191}Warning:\x19%(info_col)s} The room is now not anonymous. (public JID)' % info_col,
InfoMessage(
'\x191}Warning:\x19%(info_col)s} The room is now not anonymous. (public JID)' % info_col
),
typ=2)
elif semi_anon:
tab.add_message(
'\x19%(info_col)s}Info: The room is now semi-anonymous. (moderators-only JID)' % info_col,
InfoMessage(
'Info: The room is now semi-anonymous. (moderators-only JID)',
),
typ=2)
elif full_anon:
tab.add_message(
'\x19%(info_col)s}Info: The room is now fully anonymous.' % info_col,
InfoMessage(
'Info: The room is now fully anonymous.',
),
typ=2)
if logging_on:
tab.add_message(
'\x191}Warning: \x19%(info_col)s}This room is publicly logged' % info_col,
InfoMessage(
'\x191}Warning: \x19%(info_col)s}This room is publicly logged' % info_col
),
typ=2)
elif logging_off:
tab.add_message(
'\x19%(info_col)s}Info: This room is not logged anymore.' % info_col,
InfoMessage(
'Info: This room is not logged anymore.',
),
typ=2)
if modif:
self.core.refresh_window()
......@@ -1434,15 +1466,17 @@ class HandlerCore:
if nick_from:
tab.add_message(
"%(user)s set the subject to: \x19%(text_col)s}%(subject)s"
% fmt,
str_time=time,
InfoMessage(
"%(user)s set the subject to: \x19%(text_col)s}%(subject)s" % fmt,
time=time,
),
typ=2)
else:
tab.add_message(
"\x19%(info_col)s}The subject is: \x19%(text_col)s}%(subject)s"
% fmt,
str_time=time,
InfoMessage(
"The subject is: \x19%(text_col)s}%(subject)s" % fmt,
time=time,
),
typ=2)
tab.topic = subject