Reply/Upvote/Share Post from Category page

  • Global Moderator Plugin & Theme Dev

    @Mickola on the category page, you see topics, not posts. But replying, voting, bookmarking, and reporting all apply to posts, not topics. So which post should each of the given actions apply to? Should they all apply to the main topic post?


  • @PitaJ , reply, upvote/downvote, etc. should apply to the main post. Is it possible?

  • Global Moderator Plugin & Theme Dev

    @Mickola okay. First of all, themes are plugins, so anything you can do with a plugin, you can do with a theme.

    Voting is a more involved addition. You need template changes and some added JS. Prior discussion here https://community.nodebb.org/post/69597

    I can send some tips for the other changes later when I'm at my PC.


  • @pitaj , Happy New Year.

    I wonder if you can share your tips regarding this. Basically I want to be able to Upvote, Share, Bookmark the first/main post from the Category/Topic page.

    Thanks!

  • Global Moderator Plugin & Theme Dev


  • @pitaj , thanks. It works!

    However the bookmark function doesn't work the same as the one on the post (post-menu-list.tpl). When I clicked the bookmark button, it doesn't retain the status 'bookmarked=true'. So, when I get back to the category or refresh the page, the bookmark button is going back to the defautl OFF state. However, if I click the 'bookmark' button again, it shows an error 'You have already bookmarked this post'.

    Secondly, I'd like to show the number of bookmarks next to the button, similar to the one inside the post. I added:

    <span class="human-readable-number" component="post/bookmark-count" data-bookmarks="{./bookmarks}">{./bookmarks}</span>
    

    Also tried this one:

    <span class="human-readable-number" component="post/bookmark-count" data-bookmarks="{./posts.bookmarks}">{./posts.bookmarks}</span>
    

    Both doesn't update the bookmark count at all.

    Lastly, I'd like to add a sharing function (from post-menu-list.tpl) to category. This is what I added to topics_list.tpl

                    <!-- Share -->
                    <span class="share-tools">
                        <a href="#" data-toggle="dropdown" title="[[topic:share_this_post]]"><i class="fa fa-16px fa-fw fa-share"></i></a>
                        <ul class="dropdown-menu" role="menu">
                        
                            <!-- IF postSharing.length -->
                            <li class="dropdown-header">[[topic:share_this_post]]</li>
                            <!-- ENDIF postSharing.length -->
                            {{{each post.index.postSharing}}}
                                <li>
                                    <a role="menuitem" component="share/{postSharing.id}" tabindex="-1" href="#"><span class="menu-icon"><i class="fa fa-fw {postSharing.class}"></i></span> {postSharing.name}</a>
                                </li>
                            {{{end}}}        
                        </ul>
                    </span>
                    <!-- /Share -->
    

    It seems 'postSharing' doesn't return anything. Can you point me to the right direction?

    Thanks!

  • Global Moderator Plugin & Theme Dev

    @mickola

    However the bookmark function doesn't work the same as the one on the post (post-menu-list.tpl). When I clicked the bookmark button, it doesn't retain the status 'bookmarked=true'. So, when I get back to the category or refresh the page, the bookmark button is going back to the defautl OFF state. However, if I click the 'bookmark' button again, it shows an error 'You have already bookmarked this post'.

    This does not happen in my dev environment. How did you go about implementing the changes? And what version of NodeBB are you on?

    Secondly, I'd like to show the number of bookmarks next to the button, similar to the one inside the post.

    By default NodeBB does not provide that information along with the topic. Do you see the addition to library.js?

    library.getTopics = async (data) => {
    	const pids = data.topics.map(x => x.mainPid);
    	const [{ upvotes, downvotes }, bookmarked] = await Promise.all([
    		Posts.getVoteStatusByPostIDs(pids, data.uid),
    		Posts.hasBookmarked(pids, data.uid),
    	]);
    	data.topics.forEach((topic, i) => {
    		topic.upvoted = upvotes[i];
    		topic.downvoted = downvotes[i];
    		topic.bookmarked = bookmarked[i];
    	});
    	return data;
    };
    

    This adds the extra information to the topic for displaying whether the current user upvoted, downvoted, or bookmarked the main post. You'll need to do something similar for adding the number of bookmarks. Hint: Posts.getPostsFields(pids, ['bookmarks'])

    You'll need to do a similar thing for the postSharing stuff, which looks like you'll want Social.getActivePostSharing().

    Tip: ajaxify.data in the browser holds the data that the current page was rendered with. Use it to explore what data is available from the browser console. You can compare what's available on the topic page vs the category page and add what you need.


  • @pitaj , I implemented the changed by creating a child theme and added the scripts there. I have NodeBB 1.16.0.

    If a post hasn't been bookmarked yet, I am able to toggle it back & forth. However, if it's already bookmarked, the bookmark icon shows the off-state and when I click it, it shows an error. This is the error I got:

    Error: [[error:already-bookmarked]]
        at toggleBookmark (E:\Projects\my.forum.test\src\posts\bookmarks.js:28:10)
        at runMicrotasks (<anonymous>)
        at processTicksAndRejections (internal/process/task_queues.js:97:5)
        at async Posts.bookmark (E:\Projects\my.forum.test\src\posts\bookmarks.js:8:10)
        at async executeCommand (E:\Projects\my.forum.test\src\api\helpers.js:120:17)
        at async Object.exports.postCommand (E:\Projects\my.forum.test\src\api\helpers.js:116:9)
        at async Object.postsAPI.bookmark (E:\Projects\my.forum.test\src\api\posts.js:210:9)
        at async Posts.bookmark (E:\Projects\my.forum.test\src\controllers\write\posts.js:67:2)
        at async E:\Projects\my.forum.test\src\routes\helpers.js:34:5
    
  • Global Moderator Plugin & Theme Dev

    @mickola that's simply not the behavior I see in my environment. Are you sure you made all of the changes correctly? You could try placing a console.log in getTopics to make sure it's firing when you load a category page.

    Share your child theme theme.json and plugin.json


  • @pitaj

    I use https://github.com/nodebb/nodebb-theme-quickstart to create a child theme and added the following based on your changes.

    plugin.json:

    {
    	"id": "nodebb-theme-mytheme",
    	"main": "./lib/theme.js",
    	"hooks": [
            	{ "hook": "filter:widgets.getAreas", "method": "defineWidgetAreas" },        
    		{ "hook": "filter:category.topics.get", "method": "getTopics" }
    	],
    	"scripts": [
    		"./lib/client.js",
    		"../nodebb-theme-persona/public/persona.js",
    		"../nodebb-theme-persona/public/modules/autohidingnavbar.js",
    		"../nodebb-theme-persona/public/modules/quickreply.js"
    	],
    	"modules": {
    		"pulling.js": "node_modules/pulling/build/pulling-drawer.js"
    	}
    }
    

    theme.js

    (function(module) {
        "use strict";
        
        var Posts = require.main.require('./src/posts');
    	var Theme = {};
    
    	Theme.defineWidgetAreas = function(areas, callback) {
    		areas = areas.concat([
    			{
    				'name': 'MOTD',
    				'template': 'home.tpl',
    				'location': 'motd'
    			},
    			{
    				'name': 'Homepage Footer',
    				'template': 'home.tpl',
    				'location': 'footer'
    			},
    			{
    				'name': 'Category Sidebar',
    				'template': 'category.tpl',
    				'location': 'sidebar'
    			},
    			{
    				'name': 'Topic Footer',
    				'template': 'topic.tpl',
    				'location': 'footer'
    			}
    		]);
    
    		callback(null, areas);
        };
        
        Theme.getTopics = async (data) => {
            const pids = data.topics.map(x => x.mainPid);
            const [{ upvotes, downvotes }, bookmarked, bookmarks, sharing] = await Promise.all([
                Posts.getVoteStatusByPostIDs(pids, data.uid),
                Posts.hasBookmarked(pids, data.uid),
                Posts.getPostsFields(pids, ['bookmarks']),
                Social.getActivePostSharing(pids)
            ]);
            data.topics.forEach((topic, i) => {
                topic.upvoted = upvotes[i];
                topic.downvoted = downvotes[i];
                topic.bookmarked = bookmarked[i];
                topic.bookmarks = bookmarks[i];
                topic.sharing = sharing[i];
            });
            console.log(data);
            return data;
        };
    
        module.exports = Theme;
        
    }(module));
    

    client.js

    $(document).ready(function() {
    	require(['forum/topic/votes', 'api'], function (votes, api) {
    
    		function bookmarkPost(button) {
    			var method = button.attr('data-bookmarked') === 'false' ? 'put' : 'del';
    			var pid = button.closest('[data-pid]').attr('data-pid');
    	
    			api[method](`/posts/${pid}/bookmark`, undefined, function (err) {
    				if (err) {
    					return app.alertError(err);
    				}
    				var type = method === 'put' ? 'bookmark' : 'unbookmark';
    				$(window).trigger('action:post.' + type, { pid: pid });
    			});
    			return false;
            }
            
    		$(document).on('click', '[component="category/topic"] [component="post/upvote"]', function () {
                		console.log('upvote');
    			return votes.toggleVote($(this), '.upvoted', 1);
    		});
    		$(document).on('click', '[component="category/topic"] [component="post/downvote"]', function () {
                		console.log('downvote');
                		return votes.toggleVote($(this), '.downvoted', -1);
    		});
    		$(document).on('click', '[component="category/topic"] [component="post/bookmark"]', function () {
                		console.log('bookmark');
                		return bookmarkPost($(this));
    		});
        });
        
        $(document).on('click', '[component="category/topic"] [component="post/flag"]', function () {
            var pid = getData($(this), 'data-pid');
            require(['flags'], function (flags) {
                flags.showFlagModal({
                    type: 'post',
                    id: pid,
                });
            });
        });
    
        $(document).on('click', '[component="category/topic"] [component="post/flagUser"]', function () {
            var uid = getData($(this), 'data-uid');
            require(['flags'], function (flags) {
                flags.showFlagModal({
                    type: 'user',
                    id: uid,
                });
            });
        });
    
        function getData(button, data) {
    		return button.parents('[data-pid]').attr(data);
    	}
    
    	function togglePostVote(data) {
    		var post = $('[component="category/topic"] [data-pid="' + data.post.pid + '"]');
    		post.find('[component="post/upvote"]').toggleClass('upvoted', data.upvote);
    		post.find('[component="post/downvote"]').toggleClass('downvoted', data.downvote);
    		post.find('[component="post/vote-count"]').html(data.post.votes).attr('data-votes', data.post.votes);
    	}
    
    	socket.on('posts.upvote', togglePostVote);
    	socket.on('posts.downvote', togglePostVote);
    	socket.on('posts.unvote', togglePostVote);
    
    	function togglePostBookmark(data) {
    		var el = $('[data-pid="' + data.post.pid + '"] [component="post/bookmark"]');
    		if (!el.length) {
    			return;
    		}
    
    		el.attr('data-bookmarked', data.isBookmarked);
    
    		el.find('[component="post/bookmark/on"]').toggleClass('hidden', !data.isBookmarked);
            el.find('[component="post/bookmark/off"]').toggleClass('hidden', data.isBookmarked);
            el.find('[component="post/bookmarks"]').html(data.bookmarks).attr('data-bookmarks', data.bookmarks);
    	}
    
    	socket.on('posts.bookmark', togglePostBookmark);
    	socket.on('posts.unbookmark', togglePostBookmark);
    });
    

    Note: console.log in getTopics isn't triggered.

    Originally, I tried to add the changes straight to 'nodebb-theme-persona', but the result is still the same (and console.log in getTopics doesn't seem to be executed).

  • Global Moderator Plugin & Theme Dev

    @mickola

    Note: console.log in getTopics isn't triggered.

    Well that would be why it's not working for you. Are there any errors on nodebb startup?

    I'm using master, it shouldn't make a difference but you could try that instead of 1.16.0


  • @pitaj , I found the issue. It's the hook! I change it from:

    { "hook": "filter:category.topics.get", "method": "getTopics" }
    

    to

    { "hook": "filter:topics.get", "method": "getTopics" }
    
  • Global Moderator Plugin & Theme Dev

    @mickola well yes, the second hook should also work, but either one should work for what you're doing.


  • This post is deleted!

Suggested Topics

| |