Decorate a topic in latest on mobile

I’m working on a theme component to block posts that contain a keyword contained in a custom user field. I’ve got this working to hide replies as well as topics when listed in latest or top on desktop view, but when viewed in the app or on mobile it will append the notice that the topic was blocked, but not apply the blocked class to the content. Blocking replies within a topic works just fine on mobile or the app.

From a look through components it didn’t look like a different .hbs was used for mobile, am I missing something else?

const discourseComputed = require("discourse-common/utils/decorators").default;

function addIgnoredTopicClass() {
    if (currentUser) {
        api.container.lookup('store:main').find('user', currentUser.username).then((user) => {
            const blocklist = user.user_fields[1];
            const case_sensitive = user.user_fields[2];
            const whole_words = user.user_fields[3];
    
            const blocked = blocklist.split(",").map(function(item) {
                if (case_sensitive) {
                    return item.trim();
                } else {
                    return item.trim().toLowerCase();
                }
            });
            let classList = this._super(...arguments);
            let elem = document.querySelectorAll("[data-topic-id='" + this.topic.id.toString() + "']")[0];
            console.log(elem);
            const title = (case_sensitive) ? this.topic.fancy_title : this.topic.fancy_title.toLowerCase();
            const excerpt = (case_sensitive) ? this.topic.excerpt : this.topic.excerpt.toLowerCase();
            let result = "";
            
            for (let index = 0; index < blocked.length; ++index) {
                    let pattern = (whole_words) ? new RegExp('(?:\\b|\\s|^)' + blocked[index] + '(?:\\b|\\s|$)') : new RegExp(blocked[index]);
                    // console.log(pattern)
                    if (pattern.test(title)) {
                        classList += "blocker-blocked"
                        const found = blocked.filter(text => title.includes(text));
                        if (found.length >= 2) {
                            const last = found.pop();
                            result = found.join(', ') + ' and ' + last;
                        } else {
                            result = found.join(', ');
                        }
                        const newNode = document.createElement("a");
                        const textNode = document.createTextNode("Blocked for containing " + result + ".");
                        newNode.classList.add("block-notice")
                        newNode.appendChild(textNode);
                        newNode.onclick = function() { showComment(this); };
                        elem.children[0].insertBefore(newNode, elem.children[0].children[0]);
                        for (let index = 0; index < elem.children.length; ++index) {
                            if (elem.children[index].classList.contains('main-link')) {
                                elem.children[index].classList.add("blocker-blocked");
                            }
                        }
                        newNode.classList.remove("blocker-blocked");
                    }
                }
            
            // console.log(classList);
            return classList;
        });
    }
}

api.modifyClass("component:topic-list-item", {
    @discourseComputed()
    unboundClassNames() {
        return addIgnoredTopicClass.call(this);
    }
});
    
api.modifyClass("component:latest-topic-list-item", {
    
    @discourseComputed()
    unboundClassNames() {
        return addIgnoredTopicClass.call(this);
    }
});

Or at least, I thought I had this working. I went to work on something else for a bit and now it’s not working anymore :thinking: Maybe having this issue with api.modifyClass?

Out of curiosity, how does this differ to the built in Watched Words feature?

1 Like

So far as I understand it, Watched Words is an admin setting that applies to all users, correct? What I’m trying to do is allow users to basically have their own watched words filter that can screen out posts that are within the forum rules but the user might not want to see for their own reasons.

Fake scenario: a pets forum might have a general thread about dealing with pet hair where the main topic isn’t tagged with a specific kind of animal. User A replies about their dog, User B replies about their cat, but User A doesn’t want to see anything about cats at all. What this theme component does is lets User A put “cat” in their blocklist, and then anywhere that “cat” appears in a topic or rely, replaces it with “Blocked for containing cats” with a click to show anyway.

It’s intended for stuff that’s on topic and within the rules of the forum in general, but that might not be tagged or comes up in replies of a thread tagged something User A does want to see. The forum that I’m migrating from had been using a little browser userscript to scan the page after loading, and one of the attractions of Discourse was the possibility of building the block function right in so it works on mobile too (the userscript we were using doesn’t run on mobile browsers). It cuts down on flagging and arguments about what belongs in general threads if users can just decide they don’t want to see X without it being a rules violation.

I don’t think I’ll post this as a proper theme component because it requires three user fields to be created in the admin panel (which I think would need to be a plugin to create automatically?). I’m on a basic hosted plan here and so not fiddling with plugins, but I can post the finished theme component code and user fields settings when I’m done.

4 Likes

That sounds awesome. :slight_smile: I can think of a couple of forums that would find that useful.

3 Likes

Yeah can see potential. As to even use a modified version to extend mute user function. As I have a single user complaining :man_facepalming: that he can still see other ppl’s responses to ppl he muted. lol

Lots of interesting potential. Will keep an eye on your project.
:vulcan_salute::sunglasses::+1:

1 Like

How would I go about figuring out where the issue is? Using the mobile mode through Chrome developer tools, it works properly, but in Chrome on an actual phone and in the Android app it doesn’t work. Using the Plugin Outlets theme component, there doesn’t appear to be a relevant outlet since I’m trying to modify within a topic-list-item itself.

Sorry if this is a basic question, this is my first time working with Ember.

1 Like

I got this working on mobile and I’ll post to theme-component once I’m approved to join. This issue with mobile was I was adding a class to the topic-list child element that had a “main-link” class, but on mobile that main-link child element is a few more steps down the hierarchy than it is on desktop.

1 Like