Skip to content

v3 & Harmony diary / thoughts / code snippets

Announcements
  • @DownPW no problems.

  • @phenomlab said in v3 / Harmony diary:

    @DownPW no problems.

    Seems I miss something 😑 @phenomlab but good start 😉

    [- https://i.imgur.com/cN0YeN0.mp4 -]

    EDIT: Maybe find

    EDIT 2: @phenomlab

    Find --> I have forget toAbove and toBottom css directives
    It’s better now, just seems toBottom doesn’t work :

    b64bdc12-435e-468d-9818-c966b4cbc148-image.png

    .toBottom {
      bottom: 0px !important;
      right: 0px!important;
    }
    
    .toAbove {
      bottom: -5px !important;
      right: 400px !important;
    }
    
  • @DownPW likely you are missing z-index on that class. As you are using position: absolute the index is needed to bring that specific element forward. By default, it’ll sit behind the progress bar meaning it cannot be clicked, but can be seen.

  • @phenomlab

    Thanks for your reply my friend 😉

    I will test tomorrow and again, it’s a very great job, I like it a lot.

  • @DownPW no problems. Let me know if you have issues and I can take a look. Can’t release the code formally yet as it’s still beta (and does have very minor bugs).

  • Not sure why, but for a while I’ve been looking at the size of the CSS file that runs the dev site, and I couldn’t understand why it was almost twice the size of what it should be in terms of overall lines.

    This eventually got the better of me and I landed up going through the file and removing what appears to be a large amount of duplicated CSS. I primarily work offline using VSCode (my go-to editor), but periodically “fix” things on the fly. The problem with this is that if you make changes online then copy these from the CSS editor in the ACP back to VSCode, they seem to be copied twice - in other words, appended to the existing file despite all text being selected, which should in fact overwrite, but it doesn’t 😱

    Just something for everyone else to watch out for. I spent a fair amount of time this afternoon purging duplicate classes, and the end result is a much leaner file.

  • Thanks to @cagatay for finding a bug with the CSS in relation to tags in DEV. This has been fixed. I also found another bug whilst addressing this one in the sense that the progress bar on mobile (and desktop, but limited intrusion) shows over the tag div when the composer is active.

    Changed the scrollbar function so that it does not fire if the composer is visible. Here’s the block of code that detects if it’s active or not

                if ($(window).scrollTop() > 0 && (!$('[component="composer"]').is(":visible"))) {
                    bar.addClass('show');
                } else {
                    bar.removeClass('show');
                }
    

    Above, we now have 2 conditions. The first is to fire only if the top marker is higher than 0 - for example, hide if we are at the top of the screen. The second condition is that we also only fire if the composer isn’t active - detected by is(":visible")

    Keep 'em coming in terms of identified bugs - reporting them is much appreciated.

  • Hello @phenomlab

    I keep working on my CSS
    On v2, I displayed user groups in topics like this :

    923b452b-29cc-4ca1-82ac-a4348d3f5312-image.png

    but I can’t seem to get any result. I can’t target the right element

    Ideas ?

    Many thanks

  • @DownPW hmm. Haven’t tried this myself (yet) but will have a look later this afternoon.

  • @DownPW This should work under v3

    a.badge.rounded-1.text-uppercase.text-truncate.text-decoration-none {
        border-radius: 999px !important;
        margin-left: -10px;
        width: 22px;
        max-width: 22px;
    }
    
  • Hi @phenomlab

    Thanks my friend. 👍
    I have this result with code you provided :

    46aa98c7-962e-4301-b90a-6b8add5b1665-image.png

    It’s better but not perfect.
    But it allowed me to target the right element

    –> Here is a code improvement for those who are interested :

    /* Desktop */
    a.badge.rounded-1.text-uppercase.text-truncate.text-decoration-none {
        border-radius: 999px !important;
        margin-right: -10px;
        padding-left: 6px;
        padding-top: 5px;
        width: 20px;
        max-width: 20px !important; 
    }
    
    .d-flex.gap-1.hidden-xs.align-items-center {
        margin-left: 15px;
    } 
    
    i.fa.fa-edit.text-muted.pointer.edit-icon {
        margin-left: 8px;
    }
    
    /* Smartphone */
    @media (max-width: 767px) {
    a.badge.rounded-1.text-uppercase.text-truncate.text-decoration-none {
        padding-left: 4px;
    }
    }
    
    

    Better result :

    5045c227-f295-49b5-b5e4-fa4367878520-image.png

    cya 🙂

  • @DownPW yeah, the CSS I provided was based on my forum, so a little tweaking is necessary. Looks great !

  • Been a bit quiet lately, so thought I’d share some new things I’m going to be adding into Sudonix for the v3 release (based on Harmony). As you probably know by now, Sudonix allows you to set the best answer for any questions you ask. I wanted to extend this capability graphically, and came up with these ideas.

    First and foremost - say you’re reading a post where someone was able to get the response they needed, and it solved their issue. The next person comes along, and gets the same experience. Wouldn’t it be nice to show your appreciation? Now you can !

    Just below the “Accepted Answer” is a newly inserted div which in fact is just a widget, but leverages the power of the NodeBB API to make decisions around which div should be presented.

    https://docs.nodebb.org/api/read/#section/Overview

    For example

    ad835bd8-c49e-467a-aa99-8e34ef616280-image.png

    And then, if you are the original poster, have received responses, but are yet to choose the best answer

    4a30728a-8812-4220-a64e-b0345d7e6fae-image.png

    And, what if you were the original poster, but had no responses? We’ve got that covered too 🙂

    49db08d8-8096-490d-9fea-7325ce19985a-image.png

    The code for all of this is relatively simple - an example is shown below

    First things first: This additional code will not work properly if you do not have nodebb-plugin-question-and-answer installed. It relies on various values being exposed which are not there if the plugin is not installed.

    The first element of code to be added needs to be in ACP->Custom Content (HTML/JS/CSS)->Custom Javascript

    $(window).on('action:ajaxify.end', function (data) {
        function updateUsername() {
            $('.getUsername .username').text(app.user.username);
            $('.topicUsername').text(app.user.username);
        }
        if (document.readyState === 'loading') {
            document.addEventListener('DOMContentLoaded', updateUsername);
        } else {
            updateUsername();
        }
    });
    

    Save.

    Then, add the below to ACP->Extend-Widgets->Topic.tpl, and use the HTML Widget (which you’ll need to drag into the place where you want it to appear - the optimum is the Topic Footer.

    <head>
      <style>
        span.topicUsername {
          text-transform: capitalize;
        }
      </style>
    </head>
      {{{ if isSolved }}}
      <div id="resolved">
        <div class="resolved-wrapper">
          <h5 class="resolved-header">Did this solution help you?</h5>
          <div class="resolved-body">
            <div class="resolved-message">Did you find the suggested solution useful? Why not <a href="https://www.buymeacoffee.com/phenomlab" target=_blank>buy me a coffee<a />? It's a nice gesture, and there's <a href="https://sudonix.com/donate">other ways to donate</a> if you wish <span class="heart">💗</span></div>
          </div>
        </div>
      </div>
      <br>
      <script>
        if (!window.jQuery) {
          window.addEventListener('load', function() {
            $(document).ready(function() {
              $("#resolved").insertAfter('.isSolved');
            })
            console.log("Total posts = " + ajaxify.data.postcount)
          })
        } else {
          $(document).ready(function() {
            $("#resolved").insertAfter('.isSolved');
          })
          console.log("Total posts = " + ajaxify.data.postcount)
        }
      </script>
      {{{ end }}}
      {{{ if (uid == loggedInUser.uid) }}}
      {{{ if isQuestion }}}
      {{{ if !isSolved }}}
      {{{ if !unreplied }}}
      <div id="unresolved">
        <div class="unresolved-wrapper">
          <h5 class="unresolved-header">Did you get the answer you needed?</h5>
          <div class="unresolved-body">
            <div class="unresolved-message">Hey <span class="topicUsername"><span class="username"></span></span>. It looks as though there have been one or more replies to your original post. <br>If a provided answer resolved an issue for you, Could you please take a moment, and select "Mark this post as the correct answer" in the corresponding response? <br><br>By doing this, it means that original posters help the rest of the community find answers to previously asked questions by identifying the correct answer. </div>
          </div>
        </div>
      </div>
      <br>
      <script>
        if (!window.jQuery) {
          window.addEventListener('load', function() {
            $(document).ready(function() {
              $("#unresolved").insertBefore('[component="topic/quickreply/container"]');
            })
          })
        } else {
          $(document).ready(function() {
            $("#unresolved").insertBefore('[component="topic/quickreply/container"]');
          })
        }
      </script>
      {{{ end }}}
      {{{ end }}}
      {{{ end }}}
      {{{ end }}}
      {{{ if unreplied }}}
      {{{ if !isSolved }}}
      {{{ if isQuestion }}}
      {{{ if (uid == loggedInUser.uid) }}}
      <div id="unreplied">
        <div class="unreplied-wrapper">
          <h5 class="unreplied-header">No response yet</h5>
          <div class="unreplied-body">
            <div class="unreplied-message">Hey <span class="topicUsername"><span class="username"></span></span>. It looks as though there are no responses yet. Don't panic though, as one will be provided as soon as possible.<br>Please do not "bump" posts. </div>
          </div>
        </div>
      </div>
      <br>
      <script>
        if (!window.jQuery) {
          window.addEventListener('load', function() {
            $(document).ready(function() {
              $("#unreplied").insertBefore('[component="topic/quickreply/container"]');
            })
          })
        } else {
          $(document).ready(function() {
            $("#unreplied").insertBefore('[component="topic/quickreply/container"]');
          })
        }
      </script>
      {{{ end }}}
      {{{ end }}}
      {{{ end }}}
      {{{ end }}}
    

    Save.

    You’ll notice some interesting features here courtesy of jQuery (and vanilla js where if the page is loaded externally, and jQuery is not loaded, it will fallback to plain, which is this part if (!window.jQuery) )

    There’s also a function that will position the newly inserted div into the right place - essentially, this command .insertBefore('[component="topic/quickreply/container"]'); . The purpose of this is to place the div message in where you’d expect to see it - for example, if the post is solved, then it should appear directly underneath the resolution, but if no replies, or if you’re the original poster and you’ve yet to choose an accepted answer, it’ll be at the bottom.

    Essentially, we are not only defining a div structure, but using logic to determine placement to ensure it gets the most attention.

    And finally, the CSS used below

    div#resolved {
        border-radius: 0.375rem;
        border: 2px solid var(--bs-resolved-bg) !important;
        margin-left: 24px;
    }
    h5.resolved-header {
        color: var(--bs-body-color);
        background: var(--bs-resolved-bg);
        padding: 6px 8px;
        padding-left: 50px;
        font-weight: 600;
        height: 40px;
        line-height: 1.8;
        z-index: 1;
        position: relative;
    }
    h5.resolved-header:before {
        content: "\f648";
        font-family: "Font Awesome 6 Pro";
        font-size: 1.5rem;
        font-weight: 300;
        line-height: 1.2;
        vertical-align: bottom;
        margin-right: 10px;
        margin-left: -35px;
    }
    .resolved-body {
        padding: 20px;
        background: var(--bs-resolved-bg-rgba);
        margin-top: -10px;
    }
    
    div#unresolved {
        border-radius: 0.375rem;
        border: 2px solid var(--bs-unresolved-bg) !important;
        margin-left: 24px;
        margin-top: 20px;
    }
    h5.unresolved-header {
        color: var(--bs-body-color);
        background: var(--bs-unresolved-bg);
        padding: 6px 8px;
        padding-left: 50px;
        font-weight: 600;
        height: 40px;
        line-height: 1.8;
        z-index: 1;
        position: relative;
    }
    h5.unresolved-header:before {
        content: "\f06a";
        font-family: "Font Awesome 6 Pro";
        font-size: 1.5rem;
        font-weight: 300;
        line-height: 1.2;
        vertical-align: bottom;
        margin-right: 10px;
        margin-left: -35px;
    }
    .unresolved-body {
        padding: 20px;
        background: var(--bs-unresolved-bg-rgba);
        margin-top: -10px;
    }
    
    div#unreplied {
        border-radius: 0.375rem;
        border: 2px solid var(--bs-unreplied-bg) !important;
        margin-left: 24px;
        margin-top: 20px;
    }
    h5.unreplied-header {
        color: var(--bs-body-color);
        background: var(--bs-unreplied-bg);
        padding: 6px 8px;
        padding-left: 50px;
        font-weight: 600;
        height: 40px;
        line-height: 1.8;
        z-index: 1;
        position: relative;
    }
    h5.unreplied-header:before {
        content: "\e36d";
        font-family: "Font Awesome 6 Pro";
        font-size: 1.5rem;
        font-weight: 300;
        line-height: 1.2;
        vertical-align: bottom;
        margin-right: 10px;
        margin-left: -35px;
    }
    .unreplied-body {
        padding: 20px;
        background: var(--bs-unreplied-bg-rgba);
        margin-top: -10px;
    }
    

    Save.

    Note that I’m using LESS variables here, so you’ll need to define your own colours if you do not use this standard. I also have a Font Awesome 6 Pro license, so you’ll need to replace that as necessary.

  • @phenomlab

    Very good job…

    I must just thinking to where put your first part code on nodeBB ACP but I want to test it!

  • @DownPW The first part of the code I supplied will be placed into a widget. In my case, it’s in the below

    c3a85356-55bb-4fc3-8d88-9321b149f162-image.png

  • @phenomlab

    I have tested it seems to work but I seem to be unable to display the username in the replies and I don’t know why for this moment 😞

    <span class=“topicUsername”> doesn’t seem to work

    image.png

  • Other things I have find when I test the code.

    This is a very rare case but if the topic is set to solved and there is no answer in it, we get this kind of thing:

    image.png

    We can imagine that this happens when the user himself would have found a solution and that him or another user did not post answers in the topic and that this one is put in solved

  • @DownPW From memory, you should be using a function that looks like this in PROD

    $(window).on('action:ajaxify.end', function (data) {
        function updateUsername() {
            $('.getUsername .username').text(app.user.username);
        }
        if (document.readyState === 'loading') {
            document.addEventListener('DOMContentLoaded', updateUsername);
        } else {
            updateUsername();
        }
        var thehours = new Date().getHours();
    	var themessage;
    	var morning = ('Good morning');
    	var afternoon = ('Good afternoon');
    	var evening = ('Good evening');
        var matched = false;
        $('#getConsent').attr("href", "/user/" + app.user.username + "/consent");
    	if (thehours >= 0 && thehours < 12) {
    		themessage = morning; 
    
    	} else if (thehours >= 12 && thehours < 17) {
    		themessage = afternoon;
    
    	} else if (thehours >= 17 && thehours < 24) {
    		themessage = evening;
    	}
            $('.getUsername').prepend(themessage);
    });
    

    You’ll need the same function in your development environment if you don’t have it already, and will need to add

    $('.topicUsername').text(app.user.username);
    

    Right after line 3, so

    $(window).on('action:ajaxify.end', function(data) {
        function updateUsername() {
            $('.getUsername .username').text(app.user.username);
            $('.topicUsername').text(app.user.username);
        }
        if (document.readyState === 'loading') {
            document.addEventListener('DOMContentLoaded', updateUsername);
        } else {
            updateUsername();
        }
        var thehours = new Date().getHours();
        var themessage;
        var morning = ('Good morning');
        var afternoon = ('Good afternoon');
        var evening = ('Good evening');
        var matched = false;
        $('#getConsent').attr("href", "/user/" + app.user.username + "/consent");
        if (thehours >= 0 && thehours < 12) {
            themessage = morning;
    
        } else if (thehours >= 12 && thehours < 17) {
            themessage = afternoon;
    
        } else if (thehours >= 17 && thehours < 24) {
            themessage = evening;
        }
        if (window.location.href.indexOf("topic") > -1) {
            //console.log("This is a topic, so hide the user welcome message");
            $('#mainbanner').hide();
        } else {
            $('.getUsername').prepend(themessage);
    
        }
    
    
        //	$('.getUsername').prepend(themessage);
    });
    

    Note that your function might look different as I recall you modifying the code I supplied to meet your own needs.

  • @DownPW Ah yes, I can see a case where that would indeed fire, and I’m able to replicate it here

    ce4c965b-5c2c-417e-918b-638ff6315d57-image.png

    Let me adjust the code and get back to you.

    EDIT: Revised widget code here

    <head>
      <style>
        span.topicUsername {
          text-transform: capitalize;
        }
      </style>
    </head>
      {{{ if isSolved }}}
      <div id="resolved">
        <div class="resolved-wrapper">
          <h5 class="resolved-header">Did this solution help you?</h5>
          <div class="resolved-body">
            <div class="resolved-message">Did you find the suggested solution useful? Why not <a href="https://www.buymeacoffee.com/phenomlab" target=_blank>buy me a coffee<a />? It's a nice gesture, and there's <a href="https://sudonix.com/donate">other ways to donate</a> if you wish <span class="heart">💗</span></div>
          </div>
        </div>
      </div>
      <br>
      <script>
        if (!window.jQuery) {
          window.addEventListener('load', function() {
            $(document).ready(function() {
              $("#resolved").insertAfter('.isSolved');
            })
            console.log("Total posts = " + ajaxify.data.postcount)
          })
        } else {
          $(document).ready(function() {
            $("#resolved").insertAfter('.isSolved');
          })
          console.log("Total posts = " + ajaxify.data.postcount)
        }
      </script>
      {{{ end }}}
      {{{ if (uid == loggedInUser.uid) }}}
      {{{ if isQuestion }}}
      {{{ if !isSolved }}}
      {{{ if !unreplied }}}
      <div id="unresolved">
        <div class="unresolved-wrapper">
          <h5 class="unresolved-header">Did you get the answer you needed?</h5>
          <div class="unresolved-body">
            <div class="unresolved-message">Hey <span class="topicUsername"><span class="username"></span></span>. It looks as though there have been one or more replies to your original post. <br>If a provided answer resolved an issue for you, Could you please take a moment, and select "Mark this post as the correct answer" in the corresponding response? <br><br>By doing this, it means that original posters help the rest of the community find answers to previously asked questions by identifying the correct answer. </div>
          </div>
        </div>
      </div>
      <br>
      <script>
        if (!window.jQuery) {
          window.addEventListener('load', function() {
            $(document).ready(function() {
              $("#unresolved").insertBefore('[component="topic/quickreply/container"]');
            })
          })
        } else {
          $(document).ready(function() {
            $("#unresolved").insertBefore('[component="topic/quickreply/container"]');
          })
        }
      </script>
      {{{ end }}}
      {{{ end }}}
      {{{ end }}}
      {{{ end }}}
      {{{ if unreplied }}}
      {{{ if !isSolved }}}
      {{{ if isQuestion }}}
      <div id="unreplied">
        <div class="unreplied-wrapper">
          <h5 class="unreplied-header">No response yet</h5>
          <div class="unreplied-body">
            <div class="unreplied-message">Hey <span class="topicUsername"><span class="username"></span></span>. It looks as though there are no responses yet. Don't panic though, as one will be provided as soon as possible.<br>Please do not "bump" posts. </div>
          </div>
        </div>
      </div>
      <br>
      <script>
        if (!window.jQuery) {
          window.addEventListener('load', function() {
            $(document).ready(function() {
              $("#unreplied").insertBefore('[component="topic/quickreply/container"]');
            })
          })
        } else {
          $(document).ready(function() {
            $("#unreplied").insertBefore('[component="topic/quickreply/container"]');
          })
        }
      </script>
      {{{ end }}}
      {{{ end }}}
      {{{ end }}}
    
  • @phenomlab said in v3 & Harmony diary / thoughts / code snippets:

    @DownPW From memory, you should be using a function that looks like this in PROD

    Ha yes exactly, seems to be forget this lol. 🤣
    With $('.topicUsername').text(app.user.username); it’s perfect 🙂


    @phenomlab said in v3 & Harmony diary / thoughts / code snippets:
    EDIT: Revised widget code here

    yes perfect, definitively better ^^


    Just play with CSS for me but it’s a very good job Mark !
    I love this functionnality a lot, who play which works together with the nodebb-plugin-question-and-answer plugin


Related Topics
  • Sidebar Widget is no longer on the side!

    Moved Solved General
    4
    2 Votes
    4 Posts
    316 Views

    @Panda said in Sidebar Widget is no longer on the side!:

    Ah, so sidebar wont work on mobile?

    Correct. If you review the docs on bootstrap, you’ll notice that it is designed on a grid system

    https://getbootstrap.com/docs/5.0/layout/grid/

    What I mean by changing the category is moving it on here to general as you posted it in bugs, when it isn’t.

  • Removing blue 'moved' tag from post

    Solved Configure
    16
    3 Votes
    16 Posts
    720 Views

    @phenomlab
    Ah, got it working!
    I reversed the CSS addition to put z index high, and then I could see another error box saying fork title must be at least 3 characters.
    So made the new fork title longer and button responded.

  • Rebranding / other changes

    Announcements
    42
    33 Votes
    42 Posts
    2k Views

    @crazycells said in Rebranding / other changes:

    thanks for the info you gave, I need to transfer this info to our dev team

    No problems - let me know if you need any other info.

  • Top Ranked Forums

    Chitchat
    9
    3 Votes
    9 Posts
    570 Views

    The real issue here is that most people consider forums to be “dead” in the sense that nobody uses them anymore, and social media groups have taken their place. Their once dominant stance in the 90’s and early 00’s will never be experienced again, but having said that, there are a number of forums that did in fact survive the social media onslaught, and still enjoy a large user base.

    Forums tend to be niche. One that immediately sticks out is Reddit - despite looking like it was designed in the 80s, it still has an enormous user base. Another is Stack Overflow, which needs no introduction. The key to any forum is the content it offers, and the more people whom contribute in terms of posting , the more popular and widely respected it becomes as a reliable source of information.

    Forums are still intensely popular with gamers, alongside those that offer tips on hacking etc.

  • ineffecient use of space on mobile

    Solved Customisation
    10
    7 Votes
    10 Posts
    671 Views

    @phenomlab Thanks 🙏

  • RSS parser script [NodeBB]

    Customisation
    47
    9 Votes
    47 Posts
    4k Views

    @phenomlab said in RSS parser script [NodeBB]:

    @jac Not yet. Sorry. Have some other work commitments I need to prioritise. Hoping to get to this over the weekend.

    No worries at all mate, thanks 👍🏻

  • Easier to read code and pre blocks

    Announcements
    1
    1 Votes
    1 Posts
    288 Views
    No one has replied
  • Platform development diary

    Announcements
    9
    1 Votes
    9 Posts
    762 Views

    It’s been a while since I checked in here. Plenty going on - mostly around rectifying small pockets of resistance between light and dark modes, plus the addition of new features such as an enhanced reputation system and the ability to create polls. Plus, there are several changes going on under the hood which are completely transparent to users or the operation of the platform.

    However, some changes mean that the platform does need to be restarted for code changes to stick and function correctly. I tend to do this during non busy periods, but sometimes, it’s unfortunately inevitable. The good news is that in most cases, a full restart takes only 20 seconds.

    More to come