Write a Steemit Web App: Part 10 - Retrieving Comments with getContentReplies()
(Previous Post: Part 9)
In the previous two posts, I demonstrated a few different ways to retrieve content (i.e., blog posts). Blog posts also have comments associated with them, which is where the interactions between authors and readers take place. So, let's look at what it takes to fetch the comments for a post.
Introducing getContentReplies()
Consider the following code:
steem.api.getContentAsync(author, permlink)
.then(function(post) {
steem.api.getContentRepliesAsync(author, permlink)
.then(function(replies) {
console.log(JSON.stringify(replies, null, 2);
});
});
Here, there are two "get content" functions called with the same arguments: author
and permlink
. The first function (getContent
) fetches the blog post itself that the author/permlink point to. After that content comes back, the replies (comments) for that blog post are fetched using getContentReplies
.
Sample replies array:
[
{
"id": 8529064,
"author": "neuromancer",
"permlink": "re-jfollas-write-a-steemit-web-app-part-9-retrieving-content-with-getdiscussionsby-20170728t074055777z",
"category": "steemdev",
"parent_author": "jfollas",
"parent_permlink": "write-a-steemit-web-app-part-9-retrieving-content-with-getdiscussionsby",
"title": "",
"body": "I wish I had the time to experiment...",
"json_metadata": "{\"tags\":[\"steemdev\"],\"app\":\"steemit/0.1\"}",
"last_update": "2017-07-28T07:41:06",
"created": "2017-07-28T07:41:06",
"active": "2017-07-31T07:19:48",
"last_payout": "1970-01-01T00:00:00",
"depth": 1,
"children": 2,
"net_rshares": 0,
"abs_rshares": 0,
"vote_rshares": 0,
"children_abs_rshares": 0,
"cashout_time": "2017-08-04T07:41:06",
"max_cashout_time": "1969-12-31T23:59:59",
"total_vote_weight": 0,
"reward_weight": 10000,
"total_payout_value": "0.000 SBD",
"curator_payout_value": "0.000 SBD",
"author_rewards": 0,
"net_votes": 0,
"root_comment": 8514653,
"max_accepted_payout": "1000000.000 SBD",
"percent_steem_dollars": 10000,
"allow_replies": true,
"allow_votes": true,
"allow_curation_rewards": true,
"beneficiaries": [],
"url": "/steemdev/@jfollas/write-a-steemit-web-app-part-9-retrieving-content-with-getdiscussionsby#@neuromancer/re-jfollas-write-a-steemit-web-app-part-9-retrieving-content-with-getdiscussionsby-20170728t074055777z",
"root_title": "Write a Steemit Web App: Part 9 - Retrieving Content with getDiscussionsBy*()",
"pending_payout_value": "0.000 SBD",
"total_pending_payout_value": "0.000 STEEM",
"active_votes": [],
"replies": [],
"author_reputation": "8234790010",
"promoted": "0.000 SBD",
"body_length": 0,
"reblogged_by": []
},
{ ... }
]
Replies can be nested, like a tree or a graph structure. So, each reply will have a parent and may have children.
The getContentReplies
function only returns the children of the content specified by author/permlink. In this case, we will get all comments that were made to the blog post itself, but will not get any replies to those first-level comments.
To walk the tree and retrieve the replies of replies, we must make recursive calls into getContentReplies
for each comment that indicates that it has children (using that comment's author/permlink).
An Example
Each comment object fetched with getContentReplies
will have an empty replies
array. So, why not just populate that array to hold the children of each node as we walk the tree?
let author = 'jfollas';
let permlink = 'write-a-steemit-web-app-part-9-retrieving-content-with-getdiscussionsby';
let comments = [];
// Eagerly fetch all of the descendant replies (recursively)
let fetchReplies = function (author, permlink) {
return steem.api.getContentReplies(author, permlink)
.then(function (replies) {
return Promise.map(replies, function (r) {
if (r.children > 0) {
return fetchReplies(r.author, r.permlink)
.then(function (children) {
r.replies = children;
return r;
})
} else {
return r;
}
});
});
}
steem.api.getContentAsync(author, permlink)
.then(function (post) {
return fetchReplies(author, permlink)
.then(function (comments) {
post.replies = comments;
return post;
});
})
.then(function (post) {
console.log(JSON.stringify(post, null, 2));
})
.catch(console.log);
});
Note: Bluebird.js was used in this example, and Promise
is a Bluebird promise.
Results (modified in the interest of space):
{
"id": 8514653,
"author": "jfollas",
"permlink": "write-a-steemit-web-app-part-9-retrieving-content-with-getdiscussionsby",
"category": "steemdev",
"parent_author": "",
"parent_permlink": "steemdev",
"title": "Write a Steemit Web App: Part 9 - Retrieving Content with getDiscussionsBy*()",
"body": "...",
"json_metadata": "...",
"depth": 0,
"children": 5,
"active_votes": [...],
"replies": [
{
"id": 8514812,
"author": "bikash-tutor",
"permlink": "re-jfollas-write-a-steemit-web-app-part-9-retrieving-content-with-getdiscussionsby-20170728t033311808z",
"category": "steemdev",
"parent_author": "jfollas",
"parent_permlink": "write-a-steemit-web-app-part-9-retrieving-content-with-getdiscussionsby",
"depth": 1,
"children": 0,
"replies": [],
},
{
"id": 8529064,
"author": "neuromancer",
"permlink": "re-jfollas-write-a-steemit-web-app-part-9-retrieving-content-with-getdiscussionsby-20170728t074055777z",
"category": "steemdev",
"parent_author": "jfollas",
"parent_permlink": "write-a-steemit-web-app-part-9-retrieving-content-with-getdiscussionsby",
"depth": 1,
"children": 2,
"replies": [
{
"id": 8733164,
"author": "jfollas",
"permlink": "re-neuromancer-re-jfollas-write-a-steemit-web-app-part-9-retrieving-content-with-getdiscussionsby-20170730t145341449z",
"category": "steemdev",
"parent_author": "neuromancer",
"parent_permlink": "re-jfollas-write-a-steemit-web-app-part-9-retrieving-content-with-getdiscussionsby-20170728t074055777z",
"depth": 2,
"children": 1,
"replies": [
{
"id": 8792360,
"author": "neuromancer",
"permlink": "re-jfollas-re-neuromancer-re-jfollas-write-a-steemit-web-app-part-9-retrieving-content-with-getdiscussionsby-20170731t071946486z",
"category": "steemdev",
"parent_author": "jfollas",
"parent_permlink": "re-neuromancer-re-jfollas-write-a-steemit-web-app-part-9-retrieving-content-with-getdiscussionsby-20170730t145341449z",
"depth": 3,
"children": 0,
}
],
}
],
},
{
"id": 8533500,
"author": "mkt",
"permlink": "re-jfollas-write-a-steemit-web-app-part-9-retrieving-content-with-getdiscussionsby-20170728t085844274z",
"category": "steemdev",
"parent_author": "jfollas",
"parent_permlink": "write-a-steemit-web-app-part-9-retrieving-content-with-getdiscussionsby",
"depth": 1,
"children": 0,
"replies": [],
}
],
}
Pay attention to the depth
and children
properties of each comment object. If you render comments like Steemit.com does, then depth
can be used to indicate how far to indent the comment under its parent. I used children
in the eager-fetching code above to determine whether to fetch more replies for a given node. It can also be used for user interfaces like eSteem, which does lazy-fetching (only fetches the children of a node on demand when the user clicks a button, and only render that button if the node has children).
(Next Post: Part 11)
Anyone know why
"reblogged_by": []
array is always empty?Haha, I just had to laugh at the coincidence of landing here and this is the first comment I see.
Here are some ways to get them if anyone was trying to find the
reblogged_by
entries:https://steemit.com/steemjs/@money-dreamer/10-sbd-steemjs-programming-question
Some interesting things I noted in the arrays:
last_payout
timestamp, assume due tonull
value?:max_cashout_time
timestamp:10000
:depth
andchildren
is interesting, I would've expecteddepth
to be determined by parent/child ancestry & the required rendering intention applied accordingly. Wonder if it's used/intended for anything further? Just seems like it could be used to override an objects natural position, like 'pull-left' todepth=0
...Thanks once again for sharing the field notes of your sojourns in SteemitAPI, I really enjoy them.
Yes - I assume that the Unix epoch dates are simply due to null values in this case.
And the
10000
numbers represent two decimal places (i.e.,100.00
).That comment body was truncated by me in the interest of brevity (and I added the "...").
depth
is determined by parent/child ancestry. The first level comments of a post are depth 1 (i.e., consider the post to be depth 0). The replies of those comments are depth 2, and their replies are depth 3, etc.When rendering, I probably would use nested
<ul>
so that I wouldn't necessarily have to worry about the depth, though. I only mention it in case each node was rendered individually and you had to figure out the indent level.Thanks for reading!
Thanks for clarifying, & congrats on the @steemitboard total payout received award!
Upvoted and following!
Hope i can learn more from you
Coding for you looks like its your native language! for me, coding was never a strong point but i try to improve on it.
(Can you please look at my latest post and maybe participate a bit, your help is very much needed and very much appreciated. In that project (SteePi, (iot)) i want to check for new upvotes/transfers and turn on and off a LED when there are new ones)
Upvoted and also resteemed!
http://steemd.com. I like post
Does this mean that all the protocols and functions are written in javascript and if so, what avenues do you suggest for one to learn to code at that level?
steem.js is a JavaScript wrapper around the API. It's more of a simple passthrough, though, so when you call one of the JavaScript functions, it passes along the function name and arguments to the Steem node that you are connected to, and that's where the function executes (which then sends the results back to JavaScript).
Congratulations @jfollas! You have completed some achievement on Steemit and have been rewarded with new badge(s) :
Award for the total payout received
Click on any badge to view your own Board of Honor on SteemitBoard.
For more information about SteemitBoard, click here
If you no longer want to receive notifications, reply to this comment with the word
STOP
MSSING CLOSE ")" HERE: console.log(JSON.stringify(replies, null, 2);