• 0 Votes
    1 Posts
    1 Views

    Hi phenomlab!

    I read on nodebb forum that you self host your ntfy. Would it be possible that you can write a guide for it? I tried it myself but port 80 is already in use. I could maybe configure a different port in the server.yml but then I also would have to configure Nginx I guess? Maybe you could show us the steps you did. 🙂

    I would appreciated it.

  • 3 Votes
    3 Posts
    5 Views

    Same My 8T is good for now after 2 years.

    Besides that, I don’t like Samsung’s philosophy and I don’t like their UI either: OneUI
    The only brand whose Hardware catches my eye and especially the software overlay is Nothing with Nothing OS. But I know what you think of them 🙂

  • 0 Votes
    1 Posts
    1 Views

    Hello @phenomlab

    like thread view in topics and in Chat, I got the idea of having the same principle for the material view in the home page, the recent Topics view, unread, inside topics etc…

    We could call it Material View

    The idea would be to have a button in the right sidebar (User Bar) to activate the Material view or not. The container to use would therefore be [component="sidebar/right]

    This should of course not interact with the Thread view mode in the topics nor with the Stock code of nodeBB

    To not interfere with the other 2 codes, we could name the CSS class to add .mview or .material for example

    This is the desired result if you activate the Material view button :

    – Home Page :
    a039760d-3601-4afc-a694-47de36fe4a1c-image.png

    – Recent, unread, etc…
    fc3c540e-455f-4e9a-93bc-d58ec1e2c44d-image.png

    – Topic (without thread View)
    4c790ca3-9095-4f10-b319-de6c6b4741ff-image.png

    Here the CSS code ofr apply this Chnage without Javascript Code, just CSS :

    // ------------------------------------------ // Thread CATEGORIES & TOPIC (TEST) // ------------------------------------------ /* Home Page and Recent */ @media (min-width: 576px) { [component=post], [component="category/topic"], [component="categories/category"], li[component="chat/message"], [component="chat/recent/room"], div#users-container a { background: var(--bs-post-category-wrapper); background: #f7f7f7; border-radius: var(--bs-post-category-wrapper-radius); border-radius: 0.375rem; margin-bottom: var(--bs-post-category-wrapper-margin); margin-bottom: 20px; border: 1px solid var(--bs-border-color); } } @media (min-width: 576px){ [component="category/topic"], [component="categories/category"] { margin-bottom: var(--bs-post-category-topic-margin); margin-bottom: 20px; } } @media (min-width: 576px) { ul.topics-list li, li[component="chat/message"], [component="chat/recent/room"], div#users-container a { padding: 0.75rem 0 !important; padding-left: 20px !important; padding-right: 20px !important; } } ul.topics-list li { border-bottom: 1px solid var(--bs-border-color); } @media (min-width: 1600px) { [component=category] > *, .categories-list > *, [component="groups/container"] > *, .users-container > * { transition: opacity 150ms linear 100ms, transform 150ms ease-in-out 100ms; } } @media (min-width: 1600px) { [component=category] > *, .categories-list > *, [component="groups/container"] > *, .users-container > * { visibility: visible; } } @media (min-width: 576px) { [component="categories/category"] { padding: 20px; } } /* Topics & Posts */ .page-topic .topic .posts.timeline .timeline-event, .page-topic .topic .posts.timeline > [component="post/placeholder"], .page-topic .topic .posts.timeline > [component=post] { border-left: none; /*box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15) !important;*/ transition: margin-left 0.3s ease, margin-right 0.3s ease; border: 1px solid var(--bs-border-color); background: #F7F7F7; } .post-footer.border-bottom.pb-2 { border-bottom: 0rem !important; } li.necro-post.text-muted.timeline-event.d-flex.gap-2.pt-4 { box-shadow: none !important; border-radius: 0rem !important; border: none; background: none !important; } .page-topic .topic .posts.timeline .timeline-event.highlight, .page-topic .topic .posts.timeline > [component="post/placeholder"].highlight, .page-topic .topic .posts.timeline > [component=post].highlight { border-radius: 0.375rem; } @media (min-width: 1200px) { ul[component=topic]:before { margin-left: 50px; } } @media (min-width: 1200px) { .posts-container:before { margin-left: 43px; } ul[component=topic]:after { margin-left: 43px; } } @media (min-width: 576px) { .page-topic .topic .posts.timeline [component="topic/event"].timeline-event .timeline-badge, .page-topic .topic .posts.timeline [component="topic/necro-post"].timeline-event .timeline-badge { margin-left: 1rem; } } a.post-index.text-muted.d-none.d-md-inline { margin-right: 5px; } .page-topic .topic .posts.timeline .timeline-event:last-child, .page-topic .topic .posts.timeline>[component="post/placeholder"]:last-child, .page-topic .topic .posts.timeline>[component=post]:last-child { padding-bottom: 0rem; }

    – A helping hand developing this would be super cool. Unless you don’t care at all and you don’t like the idea.

    – For now I have a beginning, the following code inspired by your previous codes but I don’t know if I’m on the right track

    // ------------------------------------------ // material View Mode // ------------------------------------------ function material() { $(document).ready(function () { // Check if the screen width is 460px or more if ($(window).width() >= 460) { // Check if the custom thread view button already exists if ($('#materialThreadViewButton').length === 0) { // Create the button for custom thread view mode with custom IDs var threadViewButton = $('<div class="material-threads-wrapper"><form class="form"><div class="form-check form-switch material-threads-wrapper"> \ <input class="form-check-input" id="materialThreadViewButton" type="checkbox" data-field="materialThreadView"> \ <label class=" d-none d-md-inline fw-semibold" for="materialThreadViewButton"></label> \ </div></form></div>'); // Append the button to the right sidebar $('[component="sidebar/right"]').append(threadViewButton); } // Check if there's a stored state for the checkbox and update it var storedState = localStorage.getItem('materialThreadViewState'); if (storedState === 'true') { $('#materialThreadViewButton').prop('checked', true); } // Toggle the class 'material' on or off when the checkbox changes state $('#materialThreadViewButton').on('change', function () { var isChecked = $(this).is(':checked'); var theTooltip = isChecked ? "Material Thread View Off" : "Material Thread View On"; // Update tooltip message // Toggle CSS rules when the button is turned on or off if (isChecked) { console.log('Material Thread view is active.'); // Apply your CSS rules here } else { console.log('Material Thread view is inactive.'); // Remove the CSS rules here } // Store the checkbox state in localStorage localStorage.setItem('materialThreadViewState', isChecked); // Update the tooltip title $(this).attr('data-original-title', theTooltip).tooltip('dispose').tooltip({ placement: 'bottom', title: theTooltip, trigger: 'hover' }); }); // Check for changes in the checkbox state when the page loads $('#materialThreadViewButton').trigger('change'); } }); } // Attach the material function to relevant events $(window).on('action:ajaxify.end', function (data) { material(); }); $(window).on('action:posts.edited', function (data) { material(); }); $(window).on('action:posts.loaded', function (data) { material(); });

    – Here the result :

    07d72e1d-8ca3-46f8-92ce-5beeeefc8c70-image.png

  • 48 Votes
    62 Posts
    2k Views

    Somehow, I knew it wouldn’t be long before AI was was being used extensively to produce indecent images of children. I find this sickening to the core.

    https://news.sky.com/story/sickening-rise-in-ai-generated-child-sex-abuse-images-inciting-paedophiles-to-commit-more-crimes-12970836

    The transport medium for this is WhatsApp, which, given it’s encryption, is a cause for significant alarm in the sense that tracking the perpetrators of these images is almost impossible. According to the article

    Meta has defended the plans, insisting it has “robust safety measures” to detect and prevent abuse while maintaining security.

    No, it doesn’t - it can’t even stop the most simple of things such as ensuring personally identifiable information doesn’t land up being provided to an unauthorized source.

  • 35 Votes
    177 Posts
    470 Views

    @JAC said in Virgin router replacement:

    Is this something virgin would do? Or is there an app I could use?

    This is an app you can use yourself. It’s in this thread somewhere 🙂

  • 8 Votes
    7 Posts
    47 Views

    @Panda said in Techies and human relationships:

    preferable in partners eyes than being obsessed with football, gambling or numerous other vices!

    Does that mean the grass isn’t necessarily greener on the other side?

  • 3 Votes
    6 Posts
    17 Views

    Translation problem. There is no translation for your language.

    You can doing the translation on Transifex and wait for an update

  • 19 Votes
    35 Posts
    65 Views

    @DownPW said in Threaded chat support for NodeBB:

    Better like this : add shadow and border-left on self answer

    Of course - you style to your own requirements and taste 🙂 I’ll commit that CSS we discussed yesterday also

  • 13 Votes
    11 Posts
    99 Views

    And so it starts. Amazon are going to introduce forced ads even for Prime customers on their platform. To remove them, you have to pay more??

    https://news.sky.com/story/amazons-prime-video-to-include-ads-from-2024-unless-you-pay-more-12967202

  • 47 Votes
    142 Posts
    365 Views

    ha ha 🙂

  • 2 Votes
    1 Posts
    12 Views

    Just seen this post pop up on Sky News

    https://news.sky.com/story/elon-musks-brain-chip-firm-given-all-clear-to-recruit-for-human-trials-12965469

    He has claimed the devices are so safe he would happily use his children as test subjects.

    Is this guy completely insane? You’d seriously use your kids as Guinea Pigs in human trials?? This guy clearly has easily more money than sense, and anyone who’d put their children in danger in the name of technology “advances” should seriously question their own ethics - and I’m honestly shocked that nobody else seems to have a comment about this.

    This entire “experiment” is dangerous to say the least in my view as there is huge potential for error. However, reading the below article where a paralyzed man was able to walk again thanks to a neuro “bridge” is truly ground breaking and life changing for that individual.

    https://news.sky.com/story/paralysed-man-walks-again-thanks-to-digital-bridge-that-wirelessly-reconnects-brain-and-spinal-cord-12888128

    However, this is reputable Swiss technology at it’s finest - Switzerland’s Lausanne University Hospital, the University of Lausanne, and the Swiss Federal Institute of Technology Lausanne were all involved in this process and the implants themselves were developed by the French Atomic Energy Commission.

    Musk’s “off the cuff” remark makes the entire process sound “cavalier” in my view and the brain isn’t something that can be manipulated without dire consequences for the patient if you get it wrong.

    I daresay there are going to agreements composed by lawyers which each recipient of this technology will need to sign so that it exonerates Neuralink and it’s executives of all responsibility should anything go wrong.

    I must admit, I’m torn here (in the sense of the Swiss experiment) - part of me finds it morally wrong to interfere with the human brain like this because of the potential for irreversible damage, although the benefits are huge, obviously life changing for the recipient, and in most cases may outweigh the risk (at what level I cannot comment not being a neurosurgeon of course).

    Interested in other views - would you offer yourself as a test subject for this? If I were in a wheelchair and couldn’t move, I probably would I think, but would need assurance that such technology and it’s associated procedure is safe, which at this stage, I’m not convinced it’s a guarantee that can be given. There are of course no real guarantees with anything these days, but this is a leap of faith that once taken, cannot be reversed if it goes wrong.

  • 9 Votes
    23 Posts
    204 Views

    @JAC yes, same here. A lot of that functionality made it’s way into Vivaldi so you will probably feel at home.

  • Widget | CSS customization

    Solved WordPress
    3 Votes
    7 Posts
    28 Views

    @Sala Yes, this one - Impreza. One time payment, and cheap for what you get

    https://themeforest.net/item/impreza-retina-responsive-wordpress-theme/6434280

    Or the X-theme, but more expensive - but similarly a one-time payment, and lifetime updates for both.

    https://theme.co/x#pricing

    The only two themes I actually trust. I have extensive experience with both.

  • 13 Votes
    30 Posts
    166 Views

    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);
  • What is this bar called?

    Solved Customisation
    36 Votes
    92 Posts
    246 Views

    This is good 👍

  • 14 Votes
    14 Posts
    292 Views

    Just circling back here as I’ve been helping @cagatay this morning on his site, and noticed that if you use a mixture of fa-brands and fa-solid then the code supplied above may produce some odd looking results.

    If this is the case, replace the function with this

    $(document).ready(function() { $.getJSON('/api/categories', function(data, status) { $.each(data.categories, function(key, value) { var iconClass = 'fa'; // Default to 'fa' if the icon type is not recognized // Check if the icon is FontAwesome Unicode if (this.icon.startsWith('&#x') || this.icon.startsWith('&#xf')) { iconClass = 'fa'; } else if (this.icon.startsWith('fab')) { iconClass = 'fab'; } var categorylist = $(" \ <li class='dropdown-item tree-root'><span class='category-menu'><i class='" + iconClass + " " + this.icon + "'></i><a style='display: inherit;' class='dropdown-item rounded-1' href='/category/" + this.slug + "'>" + this.name + "</a></span></li> \ <ul class='tree-branch' style='list-style: none;'>" + this.children.map(c => { var childIconClass = 'fa'; // Default to 'fa' for child icons // Check if the child icon is FontAwesome Unicode if (c.icon.startsWith('&#x') || c.icon.startsWith('&#xf')) { childIconClass = 'fas'; } else if (c.icon.startsWith('fab')) { childIconClass = 'fab'; } return `<li class='dropdown-item tree-node'><span class='category-menu-tree-node'><i class='${childIconClass} ${c.icon}'></i><a class='dropdown-item rounded-1' style='display: inherit;' href='/category/${c.slug}'>${c.name}</a></span></li>`; }).join(" ") + "</ul>" ); if ($(window).width() < 767) { $(".bottombar #thecategories").append(categorylist); } else { $(".sidebar-left #thecategories").append(categorylist); } }); }); });

    In fact, if you want to replace it anyway to make your experience “future proof”, you can use this code now 🙂

  • 18 Votes
    53 Posts
    773 Views

    @JAC said in KeepSolid VPN Unlimited DEAL:

    although all are going to have their own flaws one way or another,

    Exactly that - none of them are going to suit every individual purpose. You have to read the reviews, decide what functionality you need, and then make a judgement call for yourself (and your own unique needs).

    There is no “wrong or right”, although I would steer clear of those that do not have a no-logs policy for privacy reasons alone.

  • Upgrade issues

    Solved Configure
    2 Votes
    2 Posts
    22 Views

    Use this code

    git fetch # Grab the latest code from the NodeBB repository git checkout v3.x git reset --hard origin/v3.x

    And you will have the latest version without specifying it

    https://docs.nodebb.org/configuring/upgrade/

  • 2 Votes
    5 Posts
    37 Views

    @mathourthy Good question. They have zero effect from what I can see. It’s not going to stop them from targeting anyone else.

  • hover link effect

    Solved Customisation
    6 Votes
    18 Posts
    167 Views

    @DownPW Looking at the underlying code, class start is being added on hover by jQuery in this function

    document.querySelectorAll(".button-gradient, .button-transparent").forEach((button) => { const style = getComputedStyle(button); const lines = document.createElement("div"); lines.classList.add("lines"); const groupTop = document.createElement("div"); const groupBottom = document.createElement("div"); const svg = createSVG( button.offsetWidth, button.offsetHeight, parseInt(style.borderRadius, 10) ); groupTop.appendChild(svg); groupTop.appendChild(svg.cloneNode(true)); groupTop.appendChild(svg.cloneNode(true)); groupTop.appendChild(svg.cloneNode(true)); groupBottom.appendChild(svg.cloneNode(true)); groupBottom.appendChild(svg.cloneNode(true)); groupBottom.appendChild(svg.cloneNode(true)); groupBottom.appendChild(svg.cloneNode(true)); lines.appendChild(groupTop); lines.appendChild(groupBottom); button.appendChild(lines); button.addEventListener("pointerenter", () => { button.classList.add("start"); }); svg.addEventListener("animationend", () => { button.classList.remove("start"); }); }); })

    The CSS for start is below

    .button-gradient.start .lines svg, .button-transparent.start .lines svg { animation: stroke 0.3s linear; }

    And this is the corresponding keyframe

    @keyframes stroke { 30%, 55% { opacity: 1; } 100% { stroke-dashoffset: 5; opacity: 0; } }

    It’s using both CSS and SVG, so might not be a simple affair to replicate without the SVG files.