comparison src/browser/sat_browser/blog.py @ 667:166f3b624816 frontends_multi_profiles

browser_side (blog): clean the MicroblogPanel: - remove internal constants TOGGLE_EDITION_USE_ICON and NEW_MESSAGE_USE_BUTTON - ignore the unibox and, as a consequence, remove the 'selected_entry' attribute
author souliane <souliane@mailoo.org>
date Fri, 06 Mar 2015 14:29:37 +0100
parents ebb602d8b3f2
children a90cc8fc9605
comparison
equal deleted inserted replaced
666:1bf645e73fe8 667:166f3b624816
47 import libervia_widget 47 import libervia_widget
48 from constants import Const as C 48 from constants import Const as C
49 from sat_frontends.quick_frontend import quick_widgets 49 from sat_frontends.quick_frontend import quick_widgets
50 from sat_frontends.tools import jid 50 from sat_frontends.tools import jid
51 51
52 # TODO: at some point we should decide which behaviors to keep and remove these two constants
53 TOGGLE_EDITION_USE_ICON = False # set to True to use an icon inside the "toggle syntax" button
54 NEW_MESSAGE_USE_BUTTON = False # set to True to display the "New message" button instead of an empty entry
55 52
56 unicode = str # XXX: pyjamas doesn't manage unicode 53 unicode = str # XXX: pyjamas doesn't manage unicode
57 54
58 55
59 class MicroblogItem(): 56 class MicroblogItem():
110 assert isinstance(self.author, jid.JID) # FIXME: temporary 107 assert isinstance(self.author, jid.JID) # FIXME: temporary
111 self.avatar = Image(self._blog_panel.host.getAvatarURL(self.author)) # FIXME: self.author should be initially a jid.JID 108 self.avatar = Image(self._blog_panel.host.getAvatarURL(self.author)) # FIXME: self.author should be initially a jid.JID
112 entry_avatar.add(self.avatar) 109 entry_avatar.add(self.avatar)
113 self.panel.add(entry_avatar) 110 self.panel.add(entry_avatar)
114 111
115 if TOGGLE_EDITION_USE_ICON: 112 self.entry_dialog = VerticalPanel()
116 self.entry_dialog = HorizontalPanel()
117 else:
118 self.entry_dialog = VerticalPanel()
119 self.entry_dialog.setStyleName('mb_entry_dialog') 113 self.entry_dialog.setStyleName('mb_entry_dialog')
120 self.panel.add(self.entry_dialog) 114 self.panel.add(self.entry_dialog)
121 115
122 self.add(self.panel) 116 self.add(self.panel)
123 ClickHandler.__init__(self) 117 ClickHandler.__init__(self)
210 """Remove the entry if it was an empty one (used for creating a new blog post). 204 """Remove the entry if it was an empty one (used for creating a new blog post).
211 Data for the actual new blog post will be received from the bridge""" 205 Data for the actual new blog post will be received from the bridge"""
212 if self.empty: 206 if self.empty:
213 self._blog_panel.removeEntry(self.type, self.id) 207 self._blog_panel.removeEntry(self.type, self.id)
214 if self.type == 'main_item': # restore the "New message" button 208 if self.type == 'main_item': # restore the "New message" button
215 self._blog_panel.refresh() 209 self._blog_panel.addNewMessageEntry()
216 else: # allow to create a new comment 210 else: # allow to create a new comment
217 self._parent_entry._current_comment = None 211 self._parent_entry._current_comment = None
218 self.entry_dialog.setWidth('auto') 212 self.entry_dialog.setWidth('auto')
219 try: 213 try:
220 self.toggle_syntax_button.removeFromParent() 214 self.toggle_syntax_button.removeFromParent()
310 title = _('Switch to raw text edition') 304 title = _('Switch to raw text edition')
311 else: 305 else:
312 image = '<img src="media/icons/tango/actions/32/format-text-italic.png" class="richTextIcon"/>' 306 image = '<img src="media/icons/tango/actions/32/format-text-italic.png" class="richTextIcon"/>'
313 html = '<a style="color: blue;">rich text</a>' 307 html = '<a style="color: blue;">rich text</a>'
314 title = _('Switch to rich text edition') 308 title = _('Switch to rich text edition')
315 if TOGGLE_EDITION_USE_ICON: 309 entry.toggle_syntax_button = HTML(html)
316 entry.entry_dialog.setWidth('80%') 310 entry.toggle_syntax_button.addClickListener(entry.toggleContentSyntax)
317 entry.toggle_syntax_button = Button(image, entry.toggleContentSyntax) 311 entry.toggle_syntax_button.addStyleName('mb_entry_toggle_syntax')
318 entry.toggle_syntax_button.setTitle(title) 312 entry.entry_dialog.add(entry.toggle_syntax_button)
319 entry.entry_dialog.add(entry.toggle_syntax_button) 313 entry.toggle_syntax_button.setStyleAttribute('top', '-20px') # XXX: need to force CSS
320 else: 314 entry.toggle_syntax_button.setStyleAttribute('left', '-20px')
321 entry.toggle_syntax_button = HTML(html)
322 entry.toggle_syntax_button.addClickListener(entry.toggleContentSyntax)
323 entry.toggle_syntax_button.addStyleName('mb_entry_toggle_syntax')
324 entry.entry_dialog.add(entry.toggle_syntax_button)
325 entry.toggle_syntax_button.setStyleAttribute('top', '-20px') # XXX: need to force CSS
326 entry.toggle_syntax_button.setStyleAttribute('left', '-20px')
327 315
328 def toggleContentSyntax(self): 316 def toggleContentSyntax(self):
329 """Toggle the editor between raw and rich text""" 317 """Toggle the editor between raw and rich text"""
330 original_content = self.bubble.getOriginalContent() 318 original_content = self.bubble.getOriginalContent()
331 rich = not isinstance(self.bubble, richtext.RichTextEditor) 319 rich = not isinstance(self.bubble, richtext.RichTextEditor)
369 # do not mix self.targets (set of tuple of unicode) and self.accepted_groups (set of unicode) 357 # do not mix self.targets (set of tuple of unicode) and self.accepted_groups (set of unicode)
370 quick_widgets.QuickWidget.__init__(self, host, targets, C.PROF_KEY_NONE) 358 quick_widgets.QuickWidget.__init__(self, host, targets, C.PROF_KEY_NONE)
371 libervia_widget.LiberviaWidget.__init__(self, host, ", ".join(self.accepted_groups), selectable=True) 359 libervia_widget.LiberviaWidget.__init__(self, host, ", ".join(self.accepted_groups), selectable=True)
372 self.entries = {} 360 self.entries = {}
373 self.comments = {} 361 self.comments = {}
374 self.selected_entry = None
375 self.vpanel = VerticalPanel() 362 self.vpanel = VerticalPanel()
376 self.vpanel.setStyleName('microblogPanel') 363 self.vpanel.setStyleName('microblogPanel')
377 self.setWidget(self.vpanel) 364 self.setWidget(self.vpanel)
365 self.addNewMessageEntry()
378 366
379 # 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) 367 # 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)
380 self.avatarListener = self.onAvatarUpdate 368 self.avatarListener = self.onAvatarUpdate
381 host.addListener('avatar', self.avatarListener, [C.PROF_KEY_NONE]) 369 host.addListener('avatar', self.avatarListener, [C.PROF_KEY_NONE])
382 370
396 """ 384 """
397 whoami = self.host.profiles[self.profile].whoami 385 whoami = self.host.profiles[self.profile].whoami
398 if self.isJidAccepted(jid_) or jid_.bare == whoami.bare: 386 if self.isJidAccepted(jid_) or jid_.bare == whoami.bare:
399 self.updateValue('avatar', jid_, hash_) 387 self.updateValue('avatar', jid_, hash_)
400 388
401 def refresh(self): 389 def addNewMessageEntry(self):
402 """Refresh the display of this widget. If the unibox is disabled, 390 """Add an empty entry for writing a new message if needed."""
403 display the 'New message' button or an empty bubble on top of the panel""" 391 if self.getNewMainEntry():
404 if hasattr(self, 'new_button'): 392 return # there's already one
405 self.new_button.setVisible(self.host.uni_box is None) 393 data = {'id': unicode(time()),
406 return 394 'new': True,
407 if self.host.uni_box is None: 395 'author': unicode(self.host.whoami.bare),
408 def addBox(): 396 }
409 if hasattr(self, 'new_button'): 397 entry = self.addEntry(data)
410 self.new_button.setVisible(False) 398 entry.edit(True)
411 data = {'id': unicode(time()),
412 'new': True,
413 'author': unicode(self.host.whoami.bare),
414 }
415 entry = self.addEntry(data)
416 entry.edit(True)
417 if NEW_MESSAGE_USE_BUTTON:
418 self.new_button = Button("New message", listener=addBox)
419 self.new_button.setStyleName("microblogNewButton")
420 self.vpanel.insert(self.new_button, 0)
421 elif not self.getNewMainEntry():
422 addBox()
423 399
424 def getNewMainEntry(self): 400 def getNewMainEntry(self):
425 """Get the new entry being edited, or None if it doesn't exists. 401 """Get the new entry being edited, or None if it doesn't exists.
426 402
427 @return (MicroblogEntry): the new entry being edited. 403 @return (MicroblogEntry): the new entry being edited.
444 type_ = 'ALL' if not targets else 'GROUP' 420 type_ = 'ALL' if not targets else 'GROUP'
445 # XXX: pyjamas doesn't support use of cls directly 421 # XXX: pyjamas doesn't support use of cls directly
446 widget = host.displayWidget(MicroblogPanel, targets, dropped=True) 422 widget = host.displayWidget(MicroblogPanel, targets, dropped=True)
447 host.FillMicroblogPanel(widget) 423 host.FillMicroblogPanel(widget)
448 host.bridge.getMassiveLastMblogs(type_, targets, 10, profile=C.PROF_KEY_NONE, callback=widget.massiveInsert) 424 host.bridge.getMassiveLastMblogs(type_, targets, 10, profile=C.PROF_KEY_NONE, callback=widget.massiveInsert)
449 widget.refresh() # FIXME: needed ?
450 return widget 425 return widget
451 426
452 @property 427 @property
453 def accepted_groups(self): 428 def accepted_groups(self):
454 """Return a set of the accepted groups""" 429 """Return a set of the accepted groups"""
455 return set().union(*self.targets) 430 return set().union(*self.targets)
456 431
457 def getWarningData(self, comment=None): 432 def getWarningData(self, comment):
458 """ 433 """
459 @param comment: True if the composed message is a comment. If None, consider we are 434 @param comment: set to True if the composed message is a comment
460 composing from the unibox and guess the message type from self.selected_entry
461 @return: a couple (type, msg) for calling self.host.showWarning""" 435 @return: a couple (type, msg) for calling self.host.showWarning"""
462 if comment is None: # composing from the unibox
463 if self.selected_entry and not self.selected_entry.comments:
464 log.error("an item without comment is selected")
465 return ("NONE", None)
466 comment = self.selected_entry is not None
467 if comment: 436 if comment:
468 return ("PUBLIC", "This is a <span class='warningTarget'>comment</span> and keep the initial post visibility, so it is potentialy public") 437 return ("PUBLIC", "This is a <span class='warningTarget'>comment</span> and keep the initial post visibility, so it is potentialy public")
469 elif not self.accepted_groups: 438 elif not self.accepted_groups:
470 # we have a meta MicroblogPanel, we publish publicly 439 # we have a meta MicroblogPanel, we publish publicly
471 return ("PUBLIC", self.warning_msg_public) 440 return ("PUBLIC", self.warning_msg_public)
472 else: 441 else:
473 # FIXME: manage several groups 442 # FIXME: manage several groups
474 return ("GROUP", self.warning_msg_group % ' '.join(self.accepted_groups)) 443 return ("GROUP", self.warning_msg_group % ' '.join(self.accepted_groups))
475 444
476 def onTextEntered(self, text): 445 def onTextEntered(self, text):
477 if self.selected_entry: 446 if not self.accepted_groups:
478 # we are entering a comment
479 comments_url = self.selected_entry.comments
480 if not comments_url:
481 raise Exception("ERROR: the comments URL is empty")
482 self.bridge.call("sendMblogComment", None, comments_url, text, {})
483 target = ("COMMENT", comments_url)
484 elif not self.accepted_groups:
485 # we are entering a public microblog 447 # we are entering a public microblog
486 self.bridge.call("sendMblog", None, "PUBLIC", (), text, {}) 448 self.bridge.call("sendMblog", None, "PUBLIC", (), text, {})
487 else: 449 else:
488 self.bridge.call("sendMblog", None, "GROUP", tuple(self.accepted_groups), text, {}) 450 self.bridge.call("sendMblog", None, "GROUP", tuple(self.accepted_groups), text, {})
489 451
539 if condition_to_stop != reverse: # != is XOR 501 if condition_to_stop != reverse: # != is XOR
540 break 502 break
541 idx += 1 503 idx += 1
542 504
543 vpanel.insert(entry, idx) 505 vpanel.insert(entry, idx)
544
545 506
546 def addEntryIfAccepted(self, sender, groups, mblog_entry): 507 def addEntryIfAccepted(self, sender, groups, mblog_entry):
547 """Check if an entry can go in MicroblogPanel and add to it 508 """Check if an entry can go in MicroblogPanel and add to it
548 509
549 @param sender(jid.JID): jid of the entry sender 510 @param sender(jid.JID): jid of the entry sender
622 if isinstance(sub_panel, VerticalPanel): 583 if isinstance(sub_panel, VerticalPanel):
623 sub_panel.removeFromParent() 584 sub_panel.removeFromParent()
624 except IndexError: 585 except IndexError:
625 pass 586 pass
626 child.removeFromParent() 587 child.removeFromParent()
627 self.selected_entry = None
628 break 588 break
629 elif isinstance(child, VerticalPanel) and type_ == 'comment': 589 elif isinstance(child, VerticalPanel) and type_ == 'comment':
630 for comment in child.getChildren(): 590 for comment in child.getChildren():
631 if comment.id == id_: 591 if comment.id == id_:
632 comment.removeFromParent() 592 comment.removeFromParent()
633 self.selected_entry = None
634 break 593 break
635 594
636 def ensureVisible(self, entry): 595 def ensureVisible(self, entry):
637 """Scroll to an entry to ensure its visibility 596 """Scroll to an entry to ensure its visibility
638 597
650 @param ensure_visible (boolean): if True, also scroll to the entry 609 @param ensure_visible (boolean): if True, also scroll to the entry
651 """ 610 """
652 if ensure_visible: 611 if ensure_visible:
653 self.ensureVisible(entry) 612 self.ensureVisible(entry)
654 613
655 if not self.host.uni_box or not entry.comments: 614 entry.addStyleName('selected_entry') # blink the clicked entry
656 entry.addStyleName('selected_entry') # blink the clicked entry 615 clicked_entry = entry # entry may be None when the timer is done
657 clicked_entry = entry # entry may be None when the timer is done 616 Timer(500, lambda timer: clicked_entry.removeStyleName('selected_entry'))
658 Timer(500, lambda timer: clicked_entry.removeStyleName('selected_entry'))
659 if not self.host.uni_box:
660 return # unibox is disabled
661
662 # from here the previous behavior (toggle main item selection) is conserved
663 entry = entry if entry.comments else None
664 if self.selected_entry == entry:
665 entry = None
666 if self.selected_entry:
667 self.selected_entry.removeStyleName('selected_entry')
668 if entry:
669 log.debug("microblog entry selected (author=%s)" % unicode(entry.author))
670 entry.addStyleName('selected_entry')
671 self.selected_entry = entry
672 617
673 def updateValue(self, type_, jid_, value): 618 def updateValue(self, type_, jid_, value):
674 """Update a jid value in entries 619 """Update a jid value in entries
675 620
676 @param type_: one of 'avatar', 'nick' 621 @param type_: one of 'avatar', 'nick'