Skip to content

Adding a banner to chat messages

Tips
38 3 17.4k 1
  • hmm I think because you have not create the room like me but like I said, it’s not very serious

    wrote on last edited by
    #29

    @DownPW Nope. I get the same if I create the room

    323977da-f3c4-4506-9104-531fd6d4f228-image.png

  • wrote on last edited by phenomlab
    #30

    Here’s a small modification to the chatBanner function that will place the message just above the composer/reply component meaning it is pinned at the bottom and always in view as a reminder. I’ve made this change to support the threadedChat I’m currently developing

    // Chat message banner
    function chatBanner() {
        var roomName = $("h5[component='chat/header/title']").text().trim();
        var bannerContent;
        if (roomName === "Testing 3") {
            bannerContent = '<div id="chatbanner">This message will fire for chat rooms with the title of "Testing 3"</div>';
        } else {
            bannerContent = '<div id="chatbanner">This session is for <strong>private discussion only</strong> between the chosen participants. Please do <strong>not</strong> place support requests here and create a <a href="#" onclick="app.newTopic();">new topic</a> instead.</div>';
        }
        var chatMessagesContainer = $('[component="chat/system-message"]:last-of-type');
        //var existingMessages = $('[component="chat/message"]');
        var existingMessages = $('[component="chat/composer"]');   
        if (existingMessages.length === 0) {
            // If there are no messages, append the banner to the messages container
            chatMessagesContainer.first().after(bannerContent);
        } else {
            // If there are messages, add the banner after the last message
           // existingMessages.last().after(bannerContent);
           existingMessages.before(bannerContent);
        }
    }
    

    There are only two changes here:

    var existingMessages = $('[component="chat/message"]');
    

    becomes

    var existingMessages = $('[component="chat/composer"]');
    

    and

    existingMessages.last().after(bannerContent);
    

    becomes

    existingMessages.before(bannerContent);
    
  • phenomlab phenomlab referenced this topic on
  • phenomlab phenomlab referenced this topic on
  • wrote on last edited by DownPW
    #31

    hi @phenomlab

    since v3.9, widget chat room is officialy available with nodebb-widget essential.

    I use this code for now for different banenr message for different room

    // ------------------------------------------
    // Chat messages banner 
    // ------------------------------------------
    
    // Several banner for several chat
    
    function chatBanner() {
        var roomName = $("h5[component='chat/header/title']").text().trim();
        var bannerContent;
        if (roomName === "General") {
            bannerContent = '<div id="chatbanner"><i class="fa fa-fw fa-circle-info link-primary" aria-hidden="true"></i> Message 1. </div>';
        } else if (roomName === "Support") {
            bannerContent = '<div id="chatbanner"><i class="fa fa-fw fa-circle-info link-primary" aria-hidden="true"></i>  Message 2.</div>';
        } else if (roomName === "Info") {
            bannerContent = '<div id="chatbanner"><i class="fa fa-fw fa-circle-info link-primary" aria-hidden="true"></i>  Message 3</div>';
        } else if (roomName === "xxxxxxx") {
            bannerContent = '<div id="chatbanner"><i class="fa fa-fw fa-circle-info link-primary" aria-hidden="true"></i>  Message 4.</div>';
        } else if (roomName === "xxxxxxxx") {
            bannerContent = '<div id="chatbanner"><i class="fa fa-fw fa-circle-info link-primary" aria-hidden="true"></i>  Message 5.</div>';
        } else if (roomName === "xxxxxxxxx") {
            bannerContent = '<div id="chatbanner"><i class="fa fa-fw fa-circle-info link-primary" aria-hidden="true"></i>  Message 6.</div>';
        } else {
            bannerContent = '<div id="chatbanner"><i class="fa fa-fw fa-circle-info link-primary" aria-hidden="true"></i> Ce canal est une discussion privée. </div>';
        }
        var chatMessagesContainer = $('[component="chat/system-message"]:last-of-type');
        //var existingMessages = $('[component="chat/message"]');
        var existingMessages = $('[component="chat/composer"]');
    
        if (existingMessages.length === 0) {
            // If there are no messages, append the banner to the messages container
            chatMessagesContainer.first().after(bannerContent);
        } else {
            // If there are messages, add the banner after the last message
            //existingMessages.last().after(bannerContent);
            existingMessages.before(bannerContent);
        }
    }
    
    $(window).on('action:chat.loaded', function(data) {
        $(document).ready(function() {
            chatBanner()
        });
    });
    

    On the new widget, only the last message appears :

    } else {
          bannerContent = '<div id="chatbanner"><i class="fa fa-fw fa-circle-info link-primary" aria-hidden="true"></i> Ce canal est une discussion privée. </div>';
      }
    

    When I Change room on the chat room widget, no change.

    It’s possible to adapt the code for chatroom widget without bug if we don’t use the chatroom widget ?

    many thanks

  • hi @phenomlab

    since v3.9, widget chat room is officialy available with nodebb-widget essential.

    I use this code for now for different banenr message for different room

    // ------------------------------------------
    // Chat messages banner 
    // ------------------------------------------
    
    // Several banner for several chat
    
    function chatBanner() {
        var roomName = $("h5[component='chat/header/title']").text().trim();
        var bannerContent;
        if (roomName === "General") {
            bannerContent = '<div id="chatbanner"><i class="fa fa-fw fa-circle-info link-primary" aria-hidden="true"></i> Message 1. </div>';
        } else if (roomName === "Support") {
            bannerContent = '<div id="chatbanner"><i class="fa fa-fw fa-circle-info link-primary" aria-hidden="true"></i>  Message 2.</div>';
        } else if (roomName === "Info") {
            bannerContent = '<div id="chatbanner"><i class="fa fa-fw fa-circle-info link-primary" aria-hidden="true"></i>  Message 3</div>';
        } else if (roomName === "xxxxxxx") {
            bannerContent = '<div id="chatbanner"><i class="fa fa-fw fa-circle-info link-primary" aria-hidden="true"></i>  Message 4.</div>';
        } else if (roomName === "xxxxxxxx") {
            bannerContent = '<div id="chatbanner"><i class="fa fa-fw fa-circle-info link-primary" aria-hidden="true"></i>  Message 5.</div>';
        } else if (roomName === "xxxxxxxxx") {
            bannerContent = '<div id="chatbanner"><i class="fa fa-fw fa-circle-info link-primary" aria-hidden="true"></i>  Message 6.</div>';
        } else {
            bannerContent = '<div id="chatbanner"><i class="fa fa-fw fa-circle-info link-primary" aria-hidden="true"></i> Ce canal est une discussion privée. </div>';
        }
        var chatMessagesContainer = $('[component="chat/system-message"]:last-of-type');
        //var existingMessages = $('[component="chat/message"]');
        var existingMessages = $('[component="chat/composer"]');
    
        if (existingMessages.length === 0) {
            // If there are no messages, append the banner to the messages container
            chatMessagesContainer.first().after(bannerContent);
        } else {
            // If there are messages, add the banner after the last message
            //existingMessages.last().after(bannerContent);
            existingMessages.before(bannerContent);
        }
    }
    
    $(window).on('action:chat.loaded', function(data) {
        $(document).ready(function() {
            chatBanner()
        });
    });
    

    On the new widget, only the last message appears :

    } else {
          bannerContent = '<div id="chatbanner"><i class="fa fa-fw fa-circle-info link-primary" aria-hidden="true"></i> Ce canal est une discussion privée. </div>';
      }
    

    When I Change room on the chat room widget, no change.

    It’s possible to adapt the code for chatroom widget without bug if we don’t use the chatroom widget ?

    many thanks

    wrote on last edited by phenomlab
    #32

    @DownPW Possible, yes, but not using the existing code. It would need to be changed to test for multiple entries based on two distinct widget areas. This should work (it’s already applied on your DEV server)

    function chatBanner() {
        var roomName = $("h5[component='chat/header/title']").text().trim();
        var roomNameWidget = $('[id*="chat-modal"] .btn-ghost.btn-sm.dropdown-toggle').text().trim();
        var bannerContent;
        if (roomName === "General" || roomNameWidget === "General") {
            bannerContent = '<div id="chatbanner"><i class="fa fa-fw fa-circle-info link-primary" aria-hidden="true"></i> Message 1. </div>';
        } else if (roomName === "Support" || roomNameWidget === "Support")  {
            bannerContent = '<div id="chatbanner"><i class="fa fa-fw fa-circle-info link-primary" aria-hidden="true"></i> Message 2.</div>';
        } else if (roomName === "Info" || roomNameWidget === "Info")  {
            bannerContent = '<div id="chatbanner"><i class="fa fa-fw fa-circle-info link-primary" aria-hidden="true"></i> Message 3</div>';
        } else if (roomName === "xxxxxx" || roomNameWidget === "xxxxxx")  {
            bannerContent = '<div id="chatbanner"><i class="fa fa-fw fa-circle-info link-primary" aria-hidden="true"></i> Message 4</div>';
        } else if (roomName === "xxxxxx" || roomNameWidget === "xxxxxx")  {
            bannerContent = '<div id="chatbanner"><i class="fa fa-fw fa-circle-info link-primary" aria-hidden="true"></i> Message 5</div>';
        } else if (roomName === "xxxxxx" || roomNameWidget === "xxxxxx") {
            bannerContent = '<div id="chatbanner"><i class="fa fa-fw fa-circle-info link-primary" aria-hidden="true"></i> Message 6</div>';
        } else {
            bannerContent = '<div id="chatbanner"><i class="fa fa-fw fa-circle-info link-primary" aria-hidden="true"></i> Ce canal est une discussion privée. </div>';
        }
        var chatMessagesContainer = $('[component="chat/system-message"]:last-of-type');
        //var existingMessages = $('[component="chat/message"]');
        var existingMessages = $('[component="chat/composer"]');
    
        if (existingMessages.length === 0) {
            // If there are no messages, append the banner to the messages container
            chatMessagesContainer.first().after(bannerContent);
        } else {
            // If there are messages, add the banner after the last message
            //existingMessages.last().after(bannerContent);
            existingMessages.before(bannerContent);
        }
    }
    

    Here, we are using || which is essentially an OR operator. Because we cannot know the chat room ID in advance, it is necessary to use a wildcard to track it

    [id*="chat-modal"] .btn-ghost.btn-sm.dropdown-toggle
    
  • wrote on last edited by DownPW
    #33

    woooo it works perfectly.

    Bright !!

    Thanks @phenomlab

  • woooo it works perfectly.

    Bright !!

    Thanks @phenomlab

    wrote on last edited by
    #34

    @DownPW One other “bug” (well, not in the traditional sense) that I spotted was that if you are on the root page with the chat modal widget showing, and then click the chat icon, you’ll notice some undesirable effect here in the sense that the room banner does not change.

    This is expected in fact, because the chat modal window is already exposed, and therefore, the value already exists. It is possible to alter this behaviour, but it is expensive in terms of performance as you’d need to hide the chat modal first and then re-run the function so that it only collects data from resultant chat window you opened from the side bar (hope this makes sense).

  • wrote on last edited by
    #35

    hmm it all depends on whether you think that makes sense.

    I always trust your opinion

  • hmm it all depends on whether you think that makes sense.

    I always trust your opinion

    wrote on last edited by
    #36

    @DownPW Sorry, I meant how I explained it. Pictures speak a thousand words, so I can easily put together a video and you’ll see what I mean. It’s almost the perfect storm - you are asking the code to choose either selector, and yet they both exist, so the latest value will always win.

  • wrote on last edited by
    #37

    Looks very interesting Mark, I’m going to attempt to add this to my forum tomorrow 🤝.

  • @DownPW Possible, yes, but not using the existing code. It would need to be changed to test for multiple entries based on two distinct widget areas. This should work (it’s already applied on your DEV server)

    function chatBanner() {
        var roomName = $("h5[component='chat/header/title']").text().trim();
        var roomNameWidget = $('[id*="chat-modal"] .btn-ghost.btn-sm.dropdown-toggle').text().trim();
        var bannerContent;
        if (roomName === "General" || roomNameWidget === "General") {
            bannerContent = '<div id="chatbanner"><i class="fa fa-fw fa-circle-info link-primary" aria-hidden="true"></i> Message 1. </div>';
        } else if (roomName === "Support" || roomNameWidget === "Support")  {
            bannerContent = '<div id="chatbanner"><i class="fa fa-fw fa-circle-info link-primary" aria-hidden="true"></i> Message 2.</div>';
        } else if (roomName === "Info" || roomNameWidget === "Info")  {
            bannerContent = '<div id="chatbanner"><i class="fa fa-fw fa-circle-info link-primary" aria-hidden="true"></i> Message 3</div>';
        } else if (roomName === "xxxxxx" || roomNameWidget === "xxxxxx")  {
            bannerContent = '<div id="chatbanner"><i class="fa fa-fw fa-circle-info link-primary" aria-hidden="true"></i> Message 4</div>';
        } else if (roomName === "xxxxxx" || roomNameWidget === "xxxxxx")  {
            bannerContent = '<div id="chatbanner"><i class="fa fa-fw fa-circle-info link-primary" aria-hidden="true"></i> Message 5</div>';
        } else if (roomName === "xxxxxx" || roomNameWidget === "xxxxxx") {
            bannerContent = '<div id="chatbanner"><i class="fa fa-fw fa-circle-info link-primary" aria-hidden="true"></i> Message 6</div>';
        } else {
            bannerContent = '<div id="chatbanner"><i class="fa fa-fw fa-circle-info link-primary" aria-hidden="true"></i> Ce canal est une discussion privée. </div>';
        }
        var chatMessagesContainer = $('[component="chat/system-message"]:last-of-type');
        //var existingMessages = $('[component="chat/message"]');
        var existingMessages = $('[component="chat/composer"]');
    
        if (existingMessages.length === 0) {
            // If there are no messages, append the banner to the messages container
            chatMessagesContainer.first().after(bannerContent);
        } else {
            // If there are messages, add the banner after the last message
            //existingMessages.last().after(bannerContent);
            existingMessages.before(bannerContent);
        }
    }
    

    Here, we are using || which is essentially an OR operator. Because we cannot know the chat room ID in advance, it is necessary to use a wildcard to track it

    [id*="chat-modal"] .btn-ghost.btn-sm.dropdown-toggle
    
    wrote on last edited by
    #38

    @phenomlab said:

    @DownPW Possible, yes, but not using the existing code. It would need to be changed to test for multiple entries based on two distinct widget areas. This should work (it’s already applied on your DEV server)

    function chatBanner() {
        var roomName = $("h5[component='chat/header/title']").text().trim();
        var roomNameWidget = $('[id*="chat-modal"] .btn-ghost.btn-sm.dropdown-toggle').text().trim();
        var bannerContent;
        if (roomName === "General" || roomNameWidget === "General") {
            bannerContent = '<div id="chatbanner"><i class="fa fa-fw fa-circle-info link-primary" aria-hidden="true"></i> Message 1. </div>';
        } else if (roomName === "Support" || roomNameWidget === "Support")  {
            bannerContent = '<div id="chatbanner"><i class="fa fa-fw fa-circle-info link-primary" aria-hidden="true"></i> Message 2.</div>';
        } else if (roomName === "Info" || roomNameWidget === "Info")  {
            bannerContent = '<div id="chatbanner"><i class="fa fa-fw fa-circle-info link-primary" aria-hidden="true"></i> Message 3</div>';
        } else if (roomName === "xxxxxx" || roomNameWidget === "xxxxxx")  {
            bannerContent = '<div id="chatbanner"><i class="fa fa-fw fa-circle-info link-primary" aria-hidden="true"></i> Message 4</div>';
        } else if (roomName === "xxxxxx" || roomNameWidget === "xxxxxx")  {
            bannerContent = '<div id="chatbanner"><i class="fa fa-fw fa-circle-info link-primary" aria-hidden="true"></i> Message 5</div>';
        } else if (roomName === "xxxxxx" || roomNameWidget === "xxxxxx") {
            bannerContent = '<div id="chatbanner"><i class="fa fa-fw fa-circle-info link-primary" aria-hidden="true"></i> Message 6</div>';
        } else {
            bannerContent = '<div id="chatbanner"><i class="fa fa-fw fa-circle-info link-primary" aria-hidden="true"></i> Ce canal est une discussion privée. </div>';
        }
        var chatMessagesContainer = $('[component="chat/system-message"]:last-of-type');
        //var existingMessages = $('[component="chat/message"]');
        var existingMessages = $('[component="chat/composer"]');
    
        if (existingMessages.length === 0) {
            // If there are no messages, append the banner to the messages container
            chatMessagesContainer.first().after(bannerContent);
        } else {
            // If there are messages, add the banner after the last message
            //existingMessages.last().after(bannerContent);
            existingMessages.before(bannerContent);
        }
    }
    

    Here, we are using || which is essentially an OR operator. Because we cannot know the chat room ID in advance, it is necessary to use a wildcard to track it

    [id*="chat-modal"] .btn-ghost.btn-sm.dropdown-toggle
    

    I see bugs with this code and chat box widget I use on my categories page

    What was happening

    NodeBB allows multiple chat windows to be open simultaneously , the widget and the full/modal-page DM view. Both exist in the DOM at the same time.

    The original code used global jQuery selectors like $(‘[component=“chat/composer”]’) which scanned the entire page and found elements from both chat windows at once. When you opened “XY” caht while “XXY” was still open in the widget, the selectors would pick up the wrong room name or inject the banner into the wrong window.

    The key discovery was that the action:chat.loaded event passes the modal DOM element directly as data. By wrapping it in $(data) and using $modal.find(…) for every selector, all queries are scoped exclusively to the correct modal, making it impossible for two open chat windows to interfere with each other.

    FIX code (to adapt to your rooms) :

    function chatBanner(modalElement) {
        var $modal = $(modalElement);
    
        $modal.find('#chatbanner').remove();
    
        var roomName = $modal.find('[component="chat/room/name"]').text().trim();
    
        if (!roomName) {
            var placeholder = $modal.find('[component="chat/input"]').attr('placeholder') || '';
            roomName = placeholder.replace(/^Message #?/, '').trim();
        }
    
        var bannerContent;
        if (roomName === "General") {
            bannerContent = '<div id="chatbanner"><i class="fa fa-fw fa-circle-info link-primary" aria-hidden="true"></i> Chat message banner</div>';
        } else if (roomName === "xxxxxxxxxx") {
            bannerContent = '<div id="chatbanner"><i class="fa fa-fw fa-circle-info link-primary" aria-hidden="true"></i> Chat message banner</div>';
        } else if (roomName === "xxxxxxxxxx") {
            bannerContent = '<div id="chatbanner"><i class="fa fa-fw fa-circle-info link-primary" aria-hidden="true"></i> Chat message banner</div>';
        } else if (roomName === "xxxxxxxxxx") {
            bannerContent = '<div id="chatbanner"><i class="fa fa-fw fa-circle-info link-primary" aria-hidden="true"></i> Chat message banner</div>';
        } else if (roomName === "xxxxxxxxxx") {
            bannerContent = '<div id="chatbanner"><i class="fa fa-fw fa-circle-info link-primary" aria-hidden="true"></i> Chat message banner</div>';
        } else if (roomName === "Les geeks de l'espace") {
            bannerContent = '<div id="chatbanner"><i class="fa fa-fw fa-circle-info link-primary" aria-hidden="true"></i> Chat message banner</div>';
        } else {
            bannerContent = '<div id="chatbanner"><i class="fa fa-fw fa-circle-info link-primary" aria-hidden="true"></i> Chat message banner</div>';
        }
    
        $modal.find('[component="chat/composer"]').first().before(bannerContent);
    }
    
    $(window).on('action:chat.loaded', function(ev, data) {
        chatBanner(data);
    });
    

Related Topics
  • Rename videos in bulk - linux commands

    Unsolved Tips gopro linux commands
    12
    4 Votes
    12 Posts
    4k Views
    phenomlab
    @Hari Yes, that’s one (of many) I would recommend. It’s going to be easier to do this under Windows and the fact that you are already connected using SMB is a huge plus.
  • Unable to change icon in NodeBB navigation

    Tips navigation nodebb
    1
    7
    1 Votes
    1 Posts
    896 Views
    No one has replied
  • Google slow to crawl website (Search Console)

    Tips seo crawl google
    6
    1
    3 Votes
    6 Posts
    2k Views
    phenomlab
    Seems like Google is finally crawling this site. And, “crawling” in the sense that it’s still extremely slow …
  • nodebb-user-level customisation : popover element

    Solved Customisation css user level plugin custom
    15
    6 Votes
    15 Posts
    5k Views
    DownPW
    No no, I said that in the sense that he told me it was simple ^^ I was able to see that this was not the case by targeting the elements he had advised me.
  • 2 Votes
    2 Posts
    1k Views
    phenomlab
    As an aside to this, there is also the command of tasklist which will provide a list of processes running on your machine, or a remote machine you are looking to query. [image: 000a408c-cc7e-450f-8e5e-bed9a4238a05-image.png] There is also a useful list of switches below, plus the ability to format into a table, or CSV. https://ss64.com/nt/tasklist.html
  • Google Authenticator for 2FA

    Tips tips 2fa
    7
    6 Votes
    7 Posts
    3k Views
    phenomlab
    @crazycells yes, this is something I see on a daily basis and despite how shockingly simple it is to conduct SIM jacking, it seems that several of the USA based banks are reluctant to switch to at least TOTP in the same sense as the USA has been extremely slow to adopt chip and pin - something Europe has been making use of for years. And they wonder why cheque and wire fraud is rife in America.
  • Invalid CSRF on dev install

    Moved Solved Tips csrf
    10
    5 Votes
    10 Posts
    8k Views
    phenomlab
    @DownPW which version of MongoDB are you using?
  • [NodeBB] Import custom Font

    Solved Customisation custom fonts nodebb css
    12
    3 Votes
    12 Posts
    5k Views
    phenomlab
    @cagatay you’ll need to define this in the body tag (or another element if you want greater or more granular targets) - for example body { font-family: "Poppins"; font-size: 16px; } Essentially, you use the font-size CSS directive.