comparison frontends/src/quick_frontend/quick_app.py @ 1972:02d21a589be2

quick_frontend, primitivus: notifications refactoring replaced old "alerts" system by a more generic one which use listeners and can activate callbacks on notification click.
author Goffi <goffi@goffi.org>
date Mon, 27 Jun 2016 22:36:22 +0200
parents 200cd707a46d
children 19b9d3f8a6c7
comparison
equal deleted inserted replaced
1971:9421e721d5e2 1972:02d21a589be2
32 from sat_frontends.quick_frontend import quick_contact_list 32 from sat_frontends.quick_frontend import quick_contact_list
33 from sat_frontends.quick_frontend.constants import Const as C 33 from sat_frontends.quick_frontend.constants import Const as C
34 34
35 import sys 35 import sys
36 from collections import OrderedDict 36 from collections import OrderedDict
37 import time
37 38
38 try: 39 try:
39 # FIXME: to be removed when an acceptable solution is here 40 # FIXME: to be removed when an acceptable solution is here
40 unicode('') # XXX: unicode doesn't exist in pyjamas 41 unicode('') # XXX: unicode doesn't exist in pyjamas
41 except (TypeError, AttributeError): # Error raised is not the same depending on pyjsbuild options 42 except (TypeError, AttributeError): # Error raised is not the same depending on pyjsbuild options
49 cache_keys_to_get = ['avatar'] 50 cache_keys_to_get = ['avatar']
50 51
51 def __init__(self, profile): 52 def __init__(self, profile):
52 self.profile = profile 53 self.profile = profile
53 self.whoami = None 54 self.whoami = None
54 self.data = {} 55 self.notifications = {} # key: bare jid or '' for general, value: notif data
55
56 def __getitem__(self, key):
57 return self.data[key]
58
59 def __setitem__(self, key, value):
60 self.data[key] = value
61 56
62 def plug(self): 57 def plug(self):
63 """Plug the profile to the host""" 58 """Plug the profile to the host"""
64 # we get the essential params 59 # we get the essential params
65 self.bridge.asyncGetParamA("JabberID", "Connection", profile_key=self.profile, 60 self.bridge.asyncGetParamA("JabberID", "Connection", profile_key=self.profile,
266 quick_games.Quiz.registerSignals(self) 261 quick_games.Quiz.registerSignals(self)
267 quick_games.Radiocol.registerSignals(self) 262 quick_games.Radiocol.registerSignals(self)
268 263
269 self.current_action_ids = set() # FIXME: to be removed 264 self.current_action_ids = set() # FIXME: to be removed
270 self.current_action_ids_cb = {} # FIXME: to be removed 265 self.current_action_ids_cb = {} # FIXME: to be removed
266 self._notif_id = 0
267 self._notifications = OrderedDict()
271 self.media_dir = self.bridge.getConfig('', 'media_dir') 268 self.media_dir = self.bridge.getConfig('', 'media_dir')
272 self.features = None 269 self.features = None
273 270
274 @property 271 @property
275 def current_profile(self): 272 def current_profile(self):
281 278
282 @property 279 @property
283 def visible_widgets(self): 280 def visible_widgets(self):
284 """widgets currently visible (must be implemented by frontend)""" 281 """widgets currently visible (must be implemented by frontend)"""
285 raise NotImplementedError 282 raise NotImplementedError
286
287 @property
288 def alerts_count(self):
289 """Count the over whole alerts for all contact lists"""
290 # FIXME
291 # return sum([sum(clist._alerts.values()) for clist in self.contact_lists.values()])
292 283
293 def registerSignal(self, function_name, handler=None, iface="core", with_profile=True): 284 def registerSignal(self, function_name, handler=None, iface="core", with_profile=True):
294 """Register a handler for a signal 285 """Register a handler for a signal
295 286
296 @param function_name (str): name of the signal to handle 287 @param function_name (str): name of the signal to handle
328 args: (entity, avatar file, profile) 319 args: (entity, avatar file, profile)
329 - nick: called when nick data is updated 320 - nick: called when nick data is updated
330 args: (entity, new_nick, profile) 321 args: (entity, new_nick, profile)
331 - presence: called when a presence is received 322 - presence: called when a presence is received
332 args: (entity, show, priority, statuses, profile) 323 args: (entity, show, priority, statuses, profile)
324 - notification: called when a new notification is emited
325 args: (entity, notification_data, profile)
326 - notification_clear: called when notifications are cleared
327 args: (entity, type_, profile)
333 - menu: called when a menu item is added or removed 328 - menu: called when a menu item is added or removed
334 args: (type_, path, path_i18n, item) were values are: 329 args: (type_, path, path_i18n, item) were values are:
335 type_: same as in [sat.core.sat_main.SAT.importMenu] 330 type_: same as in [sat.core.sat_main.SAT.importMenu]
336 path: same as in [sat.core.sat_main.SAT.importMenu] 331 path: same as in [sat.core.sat_main.SAT.importMenu]
337 path_i18n: translated path (or None if the item is removed) 332 path_i18n: translated path (or None if the item is removed)
493 contact_list.setContact(from_jid) 488 contact_list.setContact(from_jid)
494 489
495 # we display the message in the widget 490 # we display the message in the widget
496 491
497 chat_widget.messageNew(uid, timestamp, from_jid, target, msg, subject, type_, extra, profile) 492 chat_widget.messageNew(uid, timestamp, from_jid, target, msg, subject, type_, extra, profile)
498
499 # ContactList alert
500 if not from_me:
501 visible = False
502 for widget in self.visible_widgets:
503 if isinstance(widget, quick_chat.QuickChat) and widget.manageMessage(from_jid, type_):
504 visible = True
505 break
506 if visible: # FIXME: à virer gof:
507 if self.isHidden(): # the window is hidden
508 self.updateAlertsCounter(extra_inc=1)
509 else:
510 contact_list.addAlert(from_jid.bare if type_ == C.MESS_TYPE_GROUPCHAT else from_jid)
511 493
512 def messageSend(self, to_jid, message, subject=None, mess_type="auto", extra=None, callback=None, errback=None, profile_key=C.PROF_KEY_NONE): 494 def messageSend(self, to_jid, message, subject=None, mess_type="auto", extra=None, callback=None, errback=None, profile_key=C.PROF_KEY_NONE):
513 if subject is None: 495 if subject is None:
514 subject = {} 496 subject = {}
515 if extra is None: 497 if extra is None:
600 # widget.update(occupant) 582 # widget.update(occupant)
601 # elif from_jid.bare == widget.target.bare: # roster contact or MUC occupant 583 # elif from_jid.bare == widget.target.bare: # roster contact or MUC occupant
602 # contact_list.setCache(from_jid, 'chat_state', to_display) 584 # contact_list.setCache(from_jid, 'chat_state', to_display)
603 # widget.update(from_jid) 585 # widget.update(from_jid)
604 586
587 def notify(self, type_, entity=None, message=None, subject=None, callback=None, cb_args=None, widget=None, profile=C.PROF_KEY_NONE):
588 """Trigger an event notification
589
590 @param type_(unicode): notifation kind,
591 one of C.NOTIFY_* constant or any custom type specific to frontend
592 @param entity(jid.JID, None): entity involved in the notification
593 if entity is in contact list, a indicator may be added in front of it
594 @param message(unicode, None): message of the notification
595 @param subject(unicode, None): subject of the notification
596 @param callback(callable, None): method to call when notification is selected
597 @param cb_args(list, None): list of args for callback
598 @param widget(object, None): widget where the notification happened
599 """
600 notif_dict = self.profiles[profile].notifications
601 key = '' if entity is None else entity.bare
602 type_notifs = notif_dict.setdefault(key, {}).setdefault(type_, [])
603 notif_data = {
604 'id': self._notif_id,
605 'time': time.time(),
606 'entity': entity,
607 'callback': callback,
608 'cb_args': cb_args,
609 'message': message,
610 'subject': subject,
611 }
612 if widget is not None:
613 notif_data[widget] = widget
614 type_notifs.append(notif_data)
615 self._notifications[self._notif_id] = notif_data
616 self.callListeners('notification', entity, notif_data, profile=profile)
617
618 def getNotifs(self, entity, type_=None, profile=C.PROF_KEY_NONE):
619 """return notifications for given entity
620
621 @param entity(jid.JID, None): bare jid of the entity to check
622 None to get general notifications
623 @param type_(unicode, None): notification type to filter
624 None to get all notifications
625 @return (list[dict]): list of notifications
626 """
627 notif_dict = self.profiles[profile].notifications
628 key = '' if entity is None else entity.bare
629 key_notifs = notif_dict.setdefault(key, {})
630 if type_ is not None:
631 return key_notifs.get(type_, [])
632 ret = []
633 for notifs_list in key_notifs.itervalues():
634 ret.extend(notifs_list)
635 return ret
636
637 def clearNotifs(self, entity, type_=None, profile=C.PROF_KEY_NONE):
638 """return notifications for given entity
639
640 @param entity(jid.JID, None): bare jid of the entity to check
641 None to clear general notifications (but keep entities ones)
642 @param type_(unicode, None): notification type to filter
643 None to clear all notifications
644 @return (list[dict]): list of notifications
645 """
646 notif_dict = self.profiles[profile].notifications
647 key = '' if entity is None else entity.bare
648 try:
649 if type_ is None:
650 del notif_dict[key]
651 else:
652 del notif_dict[key][type_]
653 except KeyError:
654 return
655 self.callListeners('notificationsClear', entity, type_, profile=profile)
656
605 def psEventHandler(self, category, service_s, node, event_type, data, profile): 657 def psEventHandler(self, category, service_s, node, event_type, data, profile):
606 """Called when a PubSub event is received. 658 """Called when a PubSub event is received.
607 659
608 @param category(unicode): event category (e.g. "PEP", "MICROBLOG") 660 @param category(unicode): event category (e.g. "PEP", "MICROBLOG")
609 @param service_s (unicode): pubsub service 661 @param service_s (unicode): pubsub service
686 738
687 @return bool 739 @return bool
688 """ 740 """
689 raise NotImplementedError 741 raise NotImplementedError
690 742
691 def updateAlertsCounter(self, extra_inc=0):
692 """Update the over whole alerts counter.
693
694 @param extra_inc (int): extra counter
695 """
696 pass
697
698 def paramUpdateHandler(self, name, value, namespace, profile): 743 def paramUpdateHandler(self, name, value, namespace, profile):
699 log.debug(_(u"param update: [%(namespace)s] %(name)s = %(value)s") % {'namespace': namespace, 'name': name, 'value': value}) 744 log.debug(_(u"param update: [%(namespace)s] %(name)s = %(value)s") % {'namespace': namespace, 'name': name, 'value': value})
700 if (namespace, name) == ("Connection", "JabberID"): 745 if (namespace, name) == ("Connection", "JabberID"):
701 log.debug(_(u"Changing JID to %s") % value) 746 log.debug(_(u"Changing JID to %s") % value)
702 self.profiles[profile].whoami = jid.JID(value) 747 self.profiles[profile].whoami = jid.JID(value)