# HG changeset patch # User souliane # Date 1426670224 -3600 # Node ID e876f493dccc3d003270e355dbba682b7c7c2dbc # Parent 801eb94aa869384a4ac8faad5a0b5510281791fe browser_side: follow changes made on quick_frontend for chat states and MUC symbols + minor fixes following the refactorisation: - some MUC handlers are no more needed, the presence handler is enough - move the chat states logic to quick_frontend - display MUC games symbols - remove classes contact_list.ContactsPanel, contact_panel.Occupant and contact_panel.OccupantsList - move buildPresenceStyle and setPresenceStyle to html_tools - fixes games menu callback diff -r 801eb94aa869 -r e876f493dccc src/browser/public/libervia.css --- a/src/browser/public/libervia.css Thu Mar 19 20:41:46 2015 +0100 +++ b/src/browser/public/libervia.css Wed Mar 18 10:17:04 2015 +0100 @@ -1054,17 +1054,6 @@ color: #006600; } -.occupant { - margin-top: 10px; - margin-right: 4px; - min-width: 120px; - padding: 5px 15px 5px 15px; - font-weight: bold; - background-color: #eee; - border: 1px solid #ddd; - white-space: nowrap; -} - .occupantsPanelCell { border-right: 2px dotted #ddd; padding-left: 5px; diff -r 801eb94aa869 -r e876f493dccc src/browser/sat_browser/chat.py --- a/src/browser/sat_browser/chat.py Thu Mar 19 20:41:46 2015 +0100 +++ b/src/browser/sat_browser/chat.py Wed Mar 18 10:17:04 2015 +0100 @@ -44,7 +44,6 @@ import base_panel import contact_panel import editor_widget -import contact_list from constants import Const as C import plugin_xep_0085 import game_tarot @@ -103,6 +102,9 @@ contacts_display=('resource',)) chat_area.add(self.occupants_panel) DOM.setAttribute(chat_area.getWidgetTd(self.occupants_panel), "className", "occupantsPanelCell") + # FIXME: workaround for a pyjamas issue: calling hash on a class method always return a different value if that method is defined directly within the class (with the "def" keyword) + self.presenceListener = self.onPresenceUpdate + self.host.addListener('presence', self.presenceListener, [C.PROF_KEY_NONE]) self._body.add(chat_area) self.content = AbsolutePanel() self.content.setStyleName('chatContent') @@ -113,8 +115,7 @@ self.vpanel.setCellHeight(self._body, '100%') self.addStyleName('chatPanel') self.setWidget(self.vpanel) - self.state_machine = plugin_xep_0085.ChatStateMachine(self.host, unicode(self.target)) - self._state = None + self.chat_state_machine = plugin_xep_0085.ChatStateMachine(self.host, unicode(self.target)) self.refresh() if type_ == C.CHAT_ONE2ONE: self.historyPrint(profile=self.profile) @@ -187,40 +188,45 @@ errback=self.host.sendError, profile_key=C.PROF_KEY_NONE ) - self.state_machine._onEvent("active") + self.chat_state_machine._onEvent("active") + + def onPresenceUpdate(self, entity, show, priority, statuses, profile): + """Update entity's presence status + + @param entity(jid.JID): entity updated + @param show: availability + @parap priority: resource's priority + @param statuses: dict of statuses + @param profile: %(doc_profile)s + """ + assert self.type == C.CHAT_GROUP + if entity.bare == self.target: + self.occupants_panel.setPresence(entity, show) def onQuit(self): libervia_widget.LiberviaWidget.onQuit(self) if self.type == C.CHAT_GROUP: + self.host.removeListener('presence', self.presenceListener) self.host.bridge.call('mucLeave', None, unicode(self.target.bare)) def setUserNick(self, nick): """Set the nick of the user, usefull for e.g. change the color of the user""" self.nick = nick - def setPresents(self, nicks): - """Set the occupants of a group chat. - - @param nicks (list[unicode]): sorted list of nicknames - """ - QuickChat.setPresents(self, nicks) - self.occupants_panel.setList([jid.JID(u"%s/%s" % (self.target, nick)) for nick in nicks]) - - def replaceUser(self, nick, show_info=True): + def addUser(self, nick): """Add user if it is not in the group list""" - QuickChat.replaceUser(self, nick, show_info) + QuickChat.addUser(self, nick) occupant_jid = jid.JID("%s/%s" % (unicode(self.target), nick)) self.occupants_panel.addContact(occupant_jid) - def removeUser(self, nick, show_info=True): + def removeUser(self, nick): """Remove a user from the group list""" - QuickChat.removeUser(self, nick, show_info) + QuickChat.removeUser(self, nick) occupant_jid = jid.JID("%s/%s" % (unicode(self.target), nick)) self.occupants_panel.removeContact(occupant_jid) def changeUserNick(self, old_nick, new_nick): assert self.type == C.CHAT_GROUP - # FIXME # self.occupants_panel.removeOccupant(old_nick) # self.occupants_panel.addOccupant(new_nick) self.printInfo(_("%(old_nick)s is now known as %(new_nick)s") % {'old_nick': old_nick, 'new_nick': new_nick}) @@ -279,33 +285,18 @@ self.content.add(ChatText(nick, mymess, msg, extra)) self.content_scroll.scrollToBottom() - def setState(self, state, nick=None): - """Set the chat state (XEP-0085) of the contact. Leave nick to None - to set the state for a one2one conversation, or give a nickname or - C.ALL_OCCUPANTS to set the state of a participant within a MUC. - @param state: the new chat state - @param nick: ignored for one2one, otherwise the MUC user nick or C.ALL_OCCUPANTS + def setTitle(self, title=None, extra=None): + """Refresh the title of this Chat dialog + + @param title (unicode): main title or None to use default + @param extra (dict{unicode: unicode}): extra info """ - return # FIXME - if self.type == C.CHAT_GROUP: - assert(nick) - if nick == C.ALL_OCCUPANTS: - occupants = self.occupants_panel.occupants_panel.keys() - else: - occupants = [nick] if nick in self.occupants_panel.occupants_panel else [] - for occupant in occupants: - self.occupants_panel.occupants_panel[occupant].setState(state) - else: - self._state = state - self.refreshTitle() - self.state_machine.started = not not state # start to send "composing" state from now - - def refreshTitle(self): - """Refresh the title of this Chat dialog""" - title = unicode(self.target.bare) - if self._state: - title += " (%s)".format(self._state) - self.setTitle(title) + if title is None: + title = unicode(self.target.bare) + if extra: + extra_title = ' '.join([u'({})'.format(value) for value in extra.values()]) + title = '%s %s' % (title, extra_title) + libervia_widget.LiberviaWidget.setTitle(self, title) def setConnected(self, jid_s, resource, availability, priority, statuses): """Set connection status @@ -317,11 +308,27 @@ return box = self.occupants_panel.getOccupantBox(resource) if box: - contact_list.setPresenceStyle(box, availability) + html_tools.setPresenceStyle(box, availability) + + def setOccupantStates(self, occupant_jid, states): + """Set a MUC occupant's states. - def updateChatState(self, from_jid, state): - #TODO - pass + @param occupant_jid (jid.JID): occupant to update + @param states (dict{unicode: unicode}): new states + """ + self.occupants_panel.getContactBox(occupant_jid).updateStates(states) + if 'chat_state' in states.keys(): # start/stop sending "composing" state from now + self.chat_state_machine.started = not not states['chat_state'] + + def setContactStates(self, contact_jid, states): + """Set a one2one contact's states. + + @param contact_jid (jid.JID): contact + @param states (dict{unicode: unicode}): new states + """ + self.setTitle(extra=states) + if 'chat_state' in states.keys(): # start/stop sending "composing" state from now + self.chat_state_machine.started = not not states['chat_state'] def addGamePanel(self, widget): """Insert a game panel to this Chat dialog. diff -r 801eb94aa869 -r e876f493dccc src/browser/sat_browser/contact_list.py --- a/src/browser/sat_browser/contact_list.py Thu Mar 19 20:41:46 2015 +0100 +++ b/src/browser/sat_browser/contact_list.py Wed Mar 18 10:17:04 2015 +0100 @@ -38,37 +38,6 @@ unicode = str # XXX: pyjama doesn't manage unicode -def buildPresenceStyle(presence, base_style=None): - """Return the CSS classname to be used for displaying the given presence information. - - @param presence (unicode): presence is a value in ('', 'chat', 'away', 'dnd', 'xa') - @param base_style (unicode): base classname - @return: unicode - """ - if not base_style: - base_style = "contactLabel" - return '%s-%s' % (base_style, presence or 'connected') - - -def setPresenceStyle(widget, presence, base_style=None): - """ - Set the CSS style of a contact's element according to its presence. - - @param widget (Widget): the UI element of the contact - @param presence (unicode): a value in ("", "chat", "away", "dnd", "xa"). - @param base_style (unicode): the base name of the style to apply - """ - if not hasattr(widget, 'presence_style'): - widget.presence_style = None - style = buildPresenceStyle(presence, base_style) - if style == widget.presence_style: - return - if widget.presence_style is not None: - widget.removeStyleName(widget.presence_style) - widget.addStyleName(style) - widget.presence_style = style - - class GroupLabel(libervia_widget.DragLabel, Label, ClickHandler): def __init__(self, host, group): """ @@ -132,37 +101,6 @@ return self._groups -class ContactsPanel(contact_panel.ContactsPanel): - """The contact list that is displayed on the left side.""" - - def __init__(self, host): - - def on_click(contact_jid): - self.host.displayWidget(chat.Chat, contact_jid, type_=C.CHAT_ONE2ONE) - - contact_panel.ContactsPanel.__init__(self, host, contacts_click=on_click, - contacts_menus=(C.MENU_JID_CONTEXT, C.MENU_ROSTER_JID_CONTEXT)) - - def setState(self, jid_, type_, state): - """Change the appearance of the contact, according to the state - - @param jid_ (jid.JID): jid.JID which need to change state - @param type_ (unicode): one of "availability", "messageWaiting" - @param state: - - for messageWaiting type: - True if message are waiting - - for availability type: - C.PRESENCE_UNAVAILABLE or None if not connected, else presence like RFC6121 #4.7.2.1""" - assert type_ in ('availability', 'messageWaiting') - contact_box = self.getContactBox(jid_) - if type_ == 'availability': - if state is None: - state = C.PRESENCE_UNAVAILABLE - setPresenceStyle(contact_box.label, state) - elif type_ == 'messageWaiting': - contact_box.setAlert(state) - - class ContactTitleLabel(libervia_widget.DragLabel, Label, ClickHandler): def __init__(self, host, text): @@ -187,7 +125,11 @@ self.vPanel = VerticalPanel() _title = ContactTitleLabel(host, 'Contacts') DOM.setStyleAttribute(_title.getElement(), "cursor", "pointer") - self._contacts_panel = ContactsPanel(host) + + def on_click(contact_jid): + self.host.displayWidget(chat.Chat, contact_jid, type_=C.CHAT_ONE2ONE) + + self._contacts_panel = contact_panel.ContactsPanel(host, contacts_click=on_click, contacts_menus=(C.MENU_JID_CONTEXT, C.MENU_ROSTER_JID_CONTEXT)) self._contacts_panel.setStyleName('contactPanel') # FIXME: style doesn't exists ! self._group_panel = GroupPanel(self) @@ -235,7 +177,7 @@ self._contacts_panel.setList(to_show) for jid_ in self._alerts: - self._contacts_panel.setState(jid_, "messageWaiting", True) + self._contacts_panel.getContactBox(jid_).setAlert(True) def remove(self, entity): # FIXME: SimplePanel and QuickContactList both have a 'remove' method @@ -430,10 +372,7 @@ def onPresenceUpdate(self, entity, show, priority, statuses, profile): QuickContactList.onPresenceUpdate(self, entity, show, priority, statuses, profile) - entity_bare = entity.bare - show = self.getCache(entity_bare, C.PRESENCE_SHOW) # we use cache to have the show nformation of main resource only - self._contacts_panel.setState(entity_bare, "availability", show) - self.update() # FIXME: should update the list without rebuilding it all + self._contacts_panel.setPresence(entity, show) # def updateVisibility(self, jids, groups): # """Set the widgets visibility for the given contacts and groups diff -r 801eb94aa869 -r e876f493dccc src/browser/sat_browser/contact_panel.py --- a/src/browser/sat_browser/contact_panel.py Thu Mar 19 20:41:46 2015 +0100 +++ b/src/browser/sat_browser/contact_panel.py Wed Mar 18 10:17:04 2015 +0100 @@ -24,58 +24,13 @@ log = getLogger(__name__) from sat_frontends.tools import jid -from pyjamas.ui.AbsolutePanel import AbsolutePanel from pyjamas.ui.VerticalPanel import VerticalPanel -from pyjamas.ui.HTML import HTML import html_tools import contact_widget from constants import Const as C -# FIXME: must be removed -class Occupant(HTML): - """Occupant of a MUC room""" - - def __init__(self, nick, state=None, special=""): - """ - @param nick: the user nickname - @param state: the user chate state (XEP-0085) - @param special: a string of symbols (e.g: for activities) - """ - HTML.__init__(self, StyleName="occupant") - self.nick = nick - self._state = state - self.special = special - self._refresh() - - def __str__(self): - return self.nick - - def setState(self, state): - self._state = state - self._refresh() - - def addSpecial(self, special): - """@param special: unicode""" - if special not in self.special: - self.special += special - self._refresh() - - def removeSpecials(self, special): - """@param special: unicode or list""" - if not isinstance(special, list): - special = [special] - for symbol in special: - self.special = self.special.replace(symbol, "") - self._refresh() - - def _refresh(self): - state = (' %s' % C.MUC_USER_STATES[self._state]) if self._state else '' - special = "" if len(self.special) == 0 else " %s" % self.special - self.setHTML("%s%s%s" % (html_tools.html_sanitize(self.nick), special, state)) - - class ContactsPanel(VerticalPanel): """ContactList graphic representation @@ -118,6 +73,11 @@ """ return contact_jid.bare if self.merge_resources else contact_jid + def clear(self): + """Clear all contacts.""" + self._contacts.clear() + VerticalPanel.clear(self) + def setList(self, jids): """set all contacts in the list in one shot. @@ -173,10 +133,7 @@ @param contact_jid (jid.JID): contact JID @param url (unicode): image url """ - try: - self.getContactBox(contact_jid).updateAvatar(url) - except TypeError: - pass + self.getContactBox(contact_jid).updateAvatar(url) def updateNick(self, contact_jid, new_nick): """Update the avatar of the given contact. @@ -184,64 +141,17 @@ @param contact_jid (jid.JID): contact JID @param new_nick (unicode): new nick of the contact """ - try: - self.getContactBox(contact_jid).updateNick(new_nick) - except TypeError: - pass - - - -# FIXME: must be removed and ContactsPanel must be used instead -class OccupantsList(AbsolutePanel): - """Panel user to show occupants of a room""" + self.getContactBox(contact_jid).updateNick(new_nick) - def __init__(self): - AbsolutePanel.__init__(self) - self.occupants_list = {} - self.setStyleName('occupantsList') - - def addOccupant(self, nick): - if nick in self.occupants_list: - return - _occupant = Occupant(nick) - self.occupants_list[nick] = _occupant - self.add(_occupant) - - def removeOccupant(self, nick): - try: - self.remove(self.occupants_list[nick]) - except KeyError: - log.error("trying to remove an unexisting nick") + def setPresence(self, entity, show): + """Update entity's presence. - def getOccupantBox(self, nick): - """Get the widget element of the given nick. - - @return: Occupant + @param entity(jid.JID): entity updated + @param show: availability """ - try: - return self.occupants_list[nick] - except KeyError: - return None - - def clear(self): - self.occupants_list.clear() - AbsolutePanel.clear(self) - - def updateSpecials(self, occupants=[], html=""): - """Set the specified html "symbol" to the listed occupants, - and eventually remove it from the others (if they got it). - This is used for example to visualize who is playing a game. - @param occupants: list of the occupants that need the symbol - @param html: unicode symbol (actually one character or more) - or a list to assign different symbols of the same family. - """ - index = 0 - special = html - for occupant in self.occupants_list.keys(): - if occupant in occupants: - if isinstance(html, list): - special = html[index] - index = (index + 1) % len(html) - self.occupants_list[occupant].addSpecial(special) - else: - self.occupants_list[occupant].removeSpecials(html) + if self.merge_resources: # we use cache to have the show information of main resource only + clist = self.host.contact_list + show = clist.getCache(entity.bare, C.PRESENCE_SHOW) + if show is None: + show = C.PRESENCE_UNAVAILABLE + html_tools.setPresenceStyle(self.getContactBox(entity).label, show) diff -r 801eb94aa869 -r e876f493dccc src/browser/sat_browser/contact_widget.py --- a/src/browser/sat_browser/contact_widget.py Thu Mar 19 20:41:46 2015 +0100 +++ b/src/browser/sat_browser/contact_widget.py Wed Mar 18 10:17:04 2015 +0100 @@ -74,7 +74,7 @@ if contact_raw: break if not contact_raw: - log.error(u"Counld not find a contact display for jid {jid} (display: {display})".format(jid=self.jid, display=self.display)) + log.error(u"Could not find a contact display for jid {jid} (display: {display})".format(jid=self.jid, display=self.display)) contact_raw = "UNNAMED" contact_html = html_tools.html_sanitize(contact_raw) html = "%(alert)s%(contact)s" % {'alert': alert_html, @@ -132,6 +132,7 @@ self.jid = jid_ self.label = ContactLabel(host, self.jid, display=display) self.avatar = ContactMenuBar(self, host) if plugin_menu_context else Image() + self.states = HTML("") try: # FIXME: dirty hack to force using an Image when the menu is actually empty self.avatar.items[0] except IndexError: @@ -139,12 +140,14 @@ self.updateAvatar(host.getAvatarURL(self.jid.bare)) self.add(self.avatar) self.add(self.label) + self.add(self.states) self.addClickListener(self) def setAlert(self, alert): - """Show a visual indicator + """Show a visual indicator. - @param alert: True if alert indicator show be shown""" + @param alert (bool): True if alert indicator show be shown + """ self.label.setAlert(alert) def updateAvatar(self, url): @@ -159,7 +162,14 @@ @param new_nick (unicode): new nickname to use """ - self.label.updateNick(new_nick) + self.label.updateNick(html_tools.html_sanitize(new_nick)) + + def updateStates(self, states): + """Update the states. + + @param states (dict{unicode: unicode}): new states + """ + self.states.setHTML(u''.join(states.values())) def onClick(self, sender): try: diff -r 801eb94aa869 -r e876f493dccc src/browser/sat_browser/editor_widget.py --- a/src/browser/sat_browser/editor_widget.py Thu Mar 19 20:41:46 2015 +0100 +++ b/src/browser/sat_browser/editor_widget.py Wed Mar 18 10:17:04 2015 +0100 @@ -75,7 +75,7 @@ def _onComposing(self): """Callback when the user is composing a text.""" - self.host.selected_widget.state_machine._onEvent("composing") + self.host.selected_widget.chat_state_machine._onEvent("composing") def onMouseUp(self, sender, x, y): size = (self.getOffsetWidth(), self.getOffsetHeight()) diff -r 801eb94aa869 -r e876f493dccc src/browser/sat_browser/game_radiocol.py --- a/src/browser/sat_browser/game_radiocol.py Thu Mar 19 20:41:46 2015 +0100 +++ b/src/browser/sat_browser/game_radiocol.py Wed Mar 18 10:17:04 2015 +0100 @@ -44,6 +44,9 @@ import dialog +unicode = str # XXX: pyjama doesn't manage unicode + + class MetadataPanel(FlexTable): def __init__(self): @@ -331,7 +334,7 @@ def callback(room_jid, contacts): contacts = [unicode(contact) for contact in contacts] room_jid_s = unicode(room_jid) if room_jid else '' - host.bridge.RadioCollective(contacts, room_jid_s, profile=C.PROF_KEY_NONE) + host.bridge.launchRadioCollective(contacts, room_jid_s, profile=C.PROF_KEY_NONE) dialog.RoomAndContactsChooser(host, callback, ok_button="Choose", title="Collective Radio", visible=(False, True)) diff -r 801eb94aa869 -r e876f493dccc src/browser/sat_browser/game_tarot.py --- a/src/browser/sat_browser/game_tarot.py Thu Mar 19 20:41:46 2015 +0100 +++ b/src/browser/sat_browser/game_tarot.py Wed Mar 18 10:17:04 2015 +0100 @@ -48,6 +48,9 @@ MIN_HEIGHT = 500 +unicode = str # XXX: pyjama doesn't manage unicode + + class CardWidget(TarotCard, Image, MouseHandler): """This class is used to represent a card, graphically and logically""" @@ -400,7 +403,6 @@ host.bridge.launchTarotGame(other_players, room_jid_s, profile=C.PROF_KEY_NONE) dialog.RoomAndContactsChooser(host, onPlayersSelected, 3, title="Tarot", title_invite=_(u"Please select 3 other players"), visible=(False, True)) - def gotMenus(): host.menus.addMenu(C.MENU_GLOBAL, (D_(u"Games"), D_(u"Tarot")), callback=onTarotGame) host.addListener('gotMenus', gotMenus) diff -r 801eb94aa869 -r e876f493dccc src/browser/sat_browser/html_tools.py --- a/src/browser/sat_browser/html_tools.py Thu Mar 19 20:41:46 2015 +0100 +++ b/src/browser/sat_browser/html_tools.py Wed Mar 18 10:17:04 2015 +0100 @@ -29,20 +29,55 @@ """Naive sanitization of HTML""" return html.replace('<', '<').replace('>', '>') + def html_strip(html): """Strip leading/trailing white spaces, HTML line breaks and   sequences.""" cleaned = re.sub(r"^(
| |\s)+", "", html) cleaned = re.sub(r"(
| |\s)+$", "", cleaned) return cleaned + def inlineRoot(xhtml): """ make root element inline """ doc = dom.parseString(xhtml) return xmltools.inlineRoot(doc) + def convertNewLinesToXHTML(text): return text.replace('\n', '
') + def XHTML2Text(xhtml): """Helper method to apply both html_sanitize and convertNewLinesToXHTML""" return convertNewLinesToXHTML(html_sanitize(xhtml)) + + +def buildPresenceStyle(presence, base_style=None): + """Return the CSS classname to be used for displaying the given presence information. + + @param presence (unicode): presence is a value in ('', 'chat', 'away', 'dnd', 'xa') + @param base_style (unicode): base classname + @return: unicode + """ + if not base_style: + base_style = "contactLabel" + return '%s-%s' % (base_style, presence or 'connected') + + +def setPresenceStyle(widget, presence, base_style=None): + """ + Set the CSS style of a contact's element according to its presence. + + @param widget (Widget): the UI element of the contact + @param presence (unicode): a value in ("", "chat", "away", "dnd", "xa"). + @param base_style (unicode): the base name of the style to apply + """ + if not hasattr(widget, 'presence_style'): + widget.presence_style = None + style = buildPresenceStyle(presence, base_style) + if style == widget.presence_style: + return + if widget.presence_style is not None: + widget.removeStyleName(widget.presence_style) + widget.addStyleName(style) + widget.presence_style = style diff -r 801eb94aa869 -r e876f493dccc src/browser/sat_browser/main_panel.py --- a/src/browser/sat_browser/main_panel.py Thu Mar 19 20:41:46 2015 +0100 +++ b/src/browser/sat_browser/main_panel.py Wed Mar 18 10:17:04 2015 +0100 @@ -42,7 +42,7 @@ import base_menu import libervia_widget import editor_widget -import contact_list +import html_tools from constants import Const as C @@ -171,7 +171,7 @@ self.button = self.addCategory(u"◉") presence_menu = self.button.getSubMenu() for presence, presence_i18n in C.PRESENCE.items(): - html = u' %s' % (contact_list.buildPresenceStyle(presence), presence_i18n) + html = u' %s' % (html_tools.buildPresenceStyle(presence), presence_i18n) presence_menu.addItem(html, True, base_menu.SimpleCmd(lambda presence=presence: self.changePresenceCb(presence))) self.parent_panel = parent @@ -219,7 +219,7 @@ def setPresence(self, presence): self._presence = presence - contact_list.setPresenceStyle(self.presence_bar.button, self._presence) + html_tools.setPresenceStyle(self.presence_bar.button, self._presence) def setStatus(self, status): self.status_panel.setContent({'text': status})