{"action":"created","comment":{"url":"https://api.github.com/repos/Automattic/wp-calypso/pulls/comments/53962134","id":53962134,"diff_hunk":"@@ -0,0 +1,114 @@\n+/**\n+ * External dependencies\n+ */\n+import Immutable from 'immutable';\n+\n+/***\n+ * Creates a comment target id, a concatenation of siteId and postId basically\n+ * @param {Number} siteId site identification\n+ * @param {Number} postId post identification\n+ * @returns {String} comment target id\n+ */\n+export function getCommentParentKey( siteId, postId ) {\n+\treturn `${ siteId }-${ postId }`;\n+}\n+\n+/***\n+ * Creates a request id, a concatenation of siteId, postId, and query params basically\n+ * @param {Number} siteId site identification\n+ * @param {Number} postId post identification\n+ * @param {Object} query post identification\n+ * @returns {String} request id\n+ */\n+export function createRequestId( siteId, postId, query ) {\n+\tconst queryKeys = Object.keys( query ).sort();\n+\tconst queryString = queryKeys.map( ( key ) => `${ key }=${ query[key] }` ).join( '-' );\n+\n+\treturn `${siteId}-${postId}-${ queryString }`;\n+}\n+\n+/***\n+ * Builds a comment tree of the shape\n+ * Map<id, CommentNode> {\n+ * \tchildren: List<id>, // Array of root level comments ids\n+ * }\n+ * @param {Immutable.List} comments list of comments (as built on state.comments.items) sorted by date in descending order\n+ * @returns {Immutable.Map} Immutable map comments tree instance of the shape Map<id, CommentNode>{ children: List<id> }\n+ */\n+export function buildCommentsTree( comments ) {\n+\tconst aTree = Immutable.fromJS( { children: [] } );\n+\n+\treturn aTree.withMutations( ( commentsTree ) => {\n+\t\tcomments.forEach( ( comment ) => {\n+\t\t\t// if the comment has a parent, but we haven't seen that parent yet, create a placeholder\n+\t\t\tif ( comment.get( 'parent' ) && ! commentsTree.has( comment.getIn( [ 'parent', 'ID' ] ) ) ) {\n+\t\t\t\tcommentsTree.set( comment.getIn( [ 'parent', 'ID' ] ), Immutable.fromJS( {\n+\t\t\t\t\tchildren: [],\n+\t\t\t\t\tparentId: undefined,\n+\t\t\t\t\tdata: undefined\n+\t\t\t\t} ) );\n+\t\t\t}\n+\n+\t\t\t// if it's the first time we see that comment, create it\n+\t\t\tif ( ! commentsTree.has( comment.get( 'ID' ) ) ) {\n+\t\t\t\tcommentsTree.set( comment.get( 'ID' ), Immutable.fromJS( {\n+\t\t\t\t\tchildren: [],\n+\t\t\t\t\tparentId: comment.get( 'parent' ) ? comment.getIn( [ 'parent', 'ID' ] ) : null,\n+\t\t\t\t\tdata: comment\n+\t\t\t\t} ) );\n+\t\t\t} else {\n+\t\t\t\t// we already saw that comment, so it means it's parent of some other comment, so it already has children\n+\t\t\t\t// array, so we need only update the parentId and the data\n+\t\t\t\tconst parentId = comment.get( 'parent' ) ? comment.getIn( [ 'parent', 'ID' ] ) : null;\n+\n+\t\t\t\tcommentsTree.setIn( [ comment.get( 'ID' ), 'parentId' ], parentId );\n+\t\t\t\tcommentsTree.setIn( [ comment.get( 'ID' ), 'data' ], comment );\n+\t\t\t}\n+\n+\t\t\tif ( comment.get( 'parent' ) ) {\n+\t\t\t\tcommentsTree.updateIn( [ comment.getIn( [ 'parent', 'ID' ] ), 'children' ], ( children ) => children.push( comment.get( 'ID' ) ) );\n+\t\t\t}\n+\n+\t\t\t// We check here for commentNodeChanged in order to not add the comment if we already saw it\n+\t\t\tif ( commentsTree.getIn( [ comment.get( 'ID' ), 'parentId' ] ) === null ) {\n+\t\t\t\tcommentsTree.update( 'children', ( children ) => children.push( comment.get( 'ID' ) ) );\n+\t\t\t}\n+\t\t} );// forEach comments\n+\t} ); // withMutation\n+}\n+\n+/***\n+ * Finds and modifies items in a list\n+ *\n+ * @param {Immutable.List} list the list in which find and modify\n+ * @param {Function} predicate function that returns true for an item to modify\n+ * @param {Function} updater function that returns new value for item\n+ * @param {Boolean} updateAll whether update all matches of predicate or just the first\n+ * @returns {Immutable.List} modified list\n+ */\n+export function listFindAndModify( list, predicate, updater, updateAll = false ) {","path":"client/state/comments/utils.js","position":89,"original_position":89,"commit_id":"88b2d5e3460cf694cf5caf28d5fd73fbb1a6bbf6","original_commit_id":"2b370ca37744f28151c5caec3b1219fd9ca25d3e","user":{"login":"samuelclemens","id":16884350,"avatar_url":"https://avatars.githubusercontent.com/u/16884350?v=3","gravatar_id":"","url":"https://api.github.com/users/samuelclemens","html_url":"https://github.com/samuelclemens","followers_url":"https://api.github.com/users/samuelclemens/followers","following_url":"https://api.github.com/users/samuelclemens/following{/other_user}","gists_url":"https://api.github.com/users/samuelclemens/gists{/gist_id}","starred_url":"https://api.github.com/users/samuelclemens/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/samuelclemens/subscriptions","organizations_url":"https://api.github.com/users/samuelclemens/orgs","repos_url":"https://api.github.com/users/samuelclemens/repos","events_url":"https://api.github.com/users/samuelclemens/events{/privacy}","received_events_url":"https://api.github.com/users/samuelclemens/received_events","type":"User","site_admin":false},"body":"Sure, I'll make that later tonight. ","created_at":"2016-02-24T16:12:03Z","updated_at":"2016-02-24T16:12:03Z","html_url":"https://github.com/Automattic/wp-calypso/pull/3143#discussion_r53962134","pull_request_url":"https://api.github.com/repos/Automattic/wp-calypso/pulls/3143","_links":{"self":{"href":"https://api.github.com/repos/Automattic/wp-calypso/pulls/comments/53962134"},"html":{"href":"https://github.com/Automattic/wp-calypso/pull/3143#discussion_r53962134"},"pull_request":{"href":"https://api.github.com/repos/Automattic/wp-calypso/pulls/3143"}}},"pull_request":{"url":"https://api.github.com/repos/Automattic/wp-calypso/pulls/3143","id":58523955,"html_url":"https://github.com/Automattic/wp-calypso/pull/3143","diff_url":"https://github.com/Automattic/wp-calypso/pull/3143.diff","patch_url":"https://github.com/Automattic/wp-calypso/pull/3143.patch","issue_url":"https://api.github.com/repos/Automattic/wp-calypso/issues/3143","number":3143,"state":"open","locked":false,"title":"Add redux comments store","user":{"login":"samuelclemens","id":16884350,"avatar_url":"https://avatars.githubusercontent.com/u/16884350?v=3","gravatar_id":"","url":"https://api.github.com/users/samuelclemens","html_url":"https://github.com/samuelclemens","followers_url":"https://api.github.com/users/samuelclemens/followers","following_url":"https://api.github.com/users/samuelclemens/following{/other_user}","gists_url":"https://api.github.com/users/samuelclemens/gists{/gist_id}","starred_url":"https://api.github.com/users/samuelclemens/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/samuelclemens/subscriptions","organizations_url":"https://api.github.com/users/samuelclemens/orgs","repos_url":"https://api.github.com/users/samuelclemens/repos","events_url":"https://api.github.com/users/samuelclemens/events{/privacy}","received_events_url":"https://api.github.com/users/samuelclemens/received_events","type":"User","site_admin":false},"body":"This PR lays some ground work for issues #2839, #967, and #792 .\r\nCurrent status is that the code is mainly done, what left IMO, are some style adjustments and documentation. \r\nYour opinion is appreciated.\r\n\r\n#2839:\r\nThe comments pulled by date in DESCending order, while a tree of comments is being build in the store.\r\nThis is achieved by calling `requestPostComments()` action creator, subsequent calls will fetch the earlier (older) dated comments.\r\n\r\n#967:\r\nThe comments are built in a tree-like structure, that allows us to see in each comment which are it's children and how many children the comment has.\r\n\r\nIt can be used something like [that](https://github.com/samuelclemens/wp-calypso/blob/add/comments-redux/client/reader/comments-redux/index.jsx#L36-L50):\r\n```\r\nconst commentChildrenIds = this.props.comments.getIn( [ this.props.commentId, 'children' ] ).toJS();\r\n\r\nif ( commentChildrenIds.length > 0 ) {\r\n\tif ( this.state.viewReplies ) {\r\n\t\treturn <ol>\r\n\t\t\t\t{ commentChildrenIds.reverse().map( ( commentId ) => <Comment ... /> ) }\r\n\t\t\t\t</ol>\r\n\t} else {\r\n\t\treturn <button onClick={ this.viewRepliesClickHandler.bind( this ) }> view { commentChildrenIds.length } replies</button>;\r\n\t}\r\n}\r\n```\r\n\r\n<del>\r\n#792:\r\nPolling can be done by invoking `pollComments()` action creator ( from `<Interval />` maybe? ) after first `requestPostComments()`, it'll fetch the new comments ( comments that got approved and meet the time-frame and entirely new comments ) and remove comments that got unapproved.\r\n\r\nIt is very inefficient however, and  it also can't detect edited comments.\r\n</del>\r\nNo polling support in this PR.","created_at":"2016-02-06T16:38:30Z","updated_at":"2016-02-24T16:12:03Z","closed_at":null,"merged_at":null,"merge_commit_sha":"7f023a4aebda496a70ed312dee56038bbf4488f2","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/Automattic/wp-calypso/pulls/3143/commits","review_comments_url":"https://api.github.com/repos/Automattic/wp-calypso/pulls/3143/comments","review_comment_url":"https://api.github.com/repos/Automattic/wp-calypso/pulls/comments{/number}","comments_url":"https://api.github.com/repos/Automattic/wp-calypso/issues/3143/comments","statuses_url":"https://api.github.com/repos/Automattic/wp-calypso/statuses/88b2d5e3460cf694cf5caf28d5fd73fbb1a6bbf6","head":{"label":"samuelclemens:add/redux-comments","ref":"add/redux-comments","sha":"88b2d5e3460cf694cf5caf28d5fd73fbb1a6bbf6","user":{"login":"samuelclemens","id":16884350,"avatar_url":"https://avatars.githubusercontent.com/u/16884350?v=3","gravatar_id":"","url":"https://api.github.com/users/samuelclemens","html_url":"https://github.com/samuelclemens","followers_url":"https://api.github.com/users/samuelclemens/followers","following_url":"https://api.github.com/users/samuelclemens/following{/other_user}","gists_url":"https://api.github.com/users/samuelclemens/gists{/gist_id}","starred_url":"https://api.github.com/users/samuelclemens/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/samuelclemens/subscriptions","organizations_url":"https://api.github.com/users/samuelclemens/orgs","repos_url":"https://api.github.com/users/samuelclemens/repos","events_url":"https://api.github.com/users/samuelclemens/events{/privacy}","received_events_url":"https://api.github.com/users/samuelclemens/received_events","type":"User","site_admin":false},"repo":{"id":50390378,"name":"wp-calypso","full_name":"samuelclemens/wp-calypso","owner":{"login":"samuelclemens","id":16884350,"avatar_url":"https://avatars.githubusercontent.com/u/16884350?v=3","gravatar_id":"","url":"https://api.github.com/users/samuelclemens","html_url":"https://github.com/samuelclemens","followers_url":"https://api.github.com/users/samuelclemens/followers","following_url":"https://api.github.com/users/samuelclemens/following{/other_user}","gists_url":"https://api.github.com/users/samuelclemens/gists{/gist_id}","starred_url":"https://api.github.com/users/samuelclemens/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/samuelclemens/subscriptions","organizations_url":"https://api.github.com/users/samuelclemens/orgs","repos_url":"https://api.github.com/users/samuelclemens/repos","events_url":"https://api.github.com/users/samuelclemens/events{/privacy}","received_events_url":"https://api.github.com/users/samuelclemens/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/samuelclemens/wp-calypso","description":"The new JavaScript- and API-powered WordPress.com","fork":true,"url":"https://api.github.com/repos/samuelclemens/wp-calypso","forks_url":"https://api.github.com/repos/samuelclemens/wp-calypso/forks","keys_url":"https://api.github.com/repos/samuelclemens/wp-calypso/keys{/key_id}","collaborators_url":"https://api.github.com/repos/samuelclemens/wp-calypso/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/samuelclemens/wp-calypso/teams","hooks_url":"https://api.github.com/repos/samuelclemens/wp-calypso/hooks","issue_events_url":"https://api.github.com/repos/samuelclemens/wp-calypso/issues/events{/number}","events_url":"https://api.github.com/repos/samuelclemens/wp-calypso/events","assignees_url":"https://api.github.com/repos/samuelclemens/wp-calypso/assignees{/user}","branches_url":"https://api.github.com/repos/samuelclemens/wp-calypso/branches{/branch}","tags_url":"https://api.github.com/repos/samuelclemens/wp-calypso/tags","blobs_url":"https://api.github.com/repos/samuelclemens/wp-calypso/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/samuelclemens/wp-calypso/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/samuelclemens/wp-calypso/git/refs{/sha}","trees_url":"https://api.github.com/repos/samuelclemens/wp-calypso/git/trees{/sha}","statuses_url":"https://api.github.com/repos/samuelclemens/wp-calypso/statuses/{sha}","languages_url":"https://api.github.com/repos/samuelclemens/wp-calypso/languages","stargazers_url":"https://api.github.com/repos/samuelclemens/wp-calypso/stargazers","contributors_url":"https://api.github.com/repos/samuelclemens/wp-calypso/contributors","subscribers_url":"https://api.github.com/repos/samuelclemens/wp-calypso/subscribers","subscription_url":"https://api.github.com/repos/samuelclemens/wp-calypso/subscription","commits_url":"https://api.github.com/repos/samuelclemens/wp-calypso/commits{/sha}","git_commits_url":"https://api.github.com/repos/samuelclemens/wp-calypso/git/commits{/sha}","comments_url":"https://api.github.com/repos/samuelclemens/wp-calypso/comments{/number}","issue_comment_url":"https://api.github.com/repos/samuelclemens/wp-calypso/issues/comments{/number}","contents_url":"https://api.github.com/repos/samuelclemens/wp-calypso/contents/{+path}","compare_url":"https://api.github.com/repos/samuelclemens/wp-calypso/compare/{base}...{head}","merges_url":"https://api.github.com/repos/samuelclemens/wp-calypso/merges","archive_url":"https://api.github.com/repos/samuelclemens/wp-calypso/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/samuelclemens/wp-calypso/downloads","issues_url":"https://api.github.com/repos/samuelclemens/wp-calypso/issues{/number}","pulls_url":"https://api.github.com/repos/samuelclemens/wp-calypso/pulls{/number}","milestones_url":"https://api.github.com/repos/samuelclemens/wp-calypso/milestones{/number}","notifications_url":"https://api.github.com/repos/samuelclemens/wp-calypso/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/samuelclemens/wp-calypso/labels{/name}","releases_url":"https://api.github.com/repos/samuelclemens/wp-calypso/releases{/id}","deployments_url":"https://api.github.com/repos/samuelclemens/wp-calypso/deployments","created_at":"2016-01-26T00:10:24Z","updated_at":"2016-01-26T00:10:29Z","pushed_at":"2016-02-24T05:02:53Z","git_url":"git://github.com/samuelclemens/wp-calypso.git","ssh_url":"git@github.com:samuelclemens/wp-calypso.git","clone_url":"https://github.com/samuelclemens/wp-calypso.git","svn_url":"https://github.com/samuelclemens/wp-calypso","homepage":"https://developer.wordpress.com/calypso/","size":13369,"stargazers_count":0,"watchers_count":0,"language":"JavaScript","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"Automattic:master","ref":"master","sha":"95aa72b11897a595917f6216c06492fcd370732a","user":{"login":"Automattic","id":887802,"avatar_url":"https://avatars.githubusercontent.com/u/887802?v=3","gravatar_id":"","url":"https://api.github.com/users/Automattic","html_url":"https://github.com/Automattic","followers_url":"https://api.github.com/users/Automattic/followers","following_url":"https://api.github.com/users/Automattic/following{/other_user}","gists_url":"https://api.github.com/users/Automattic/gists{/gist_id}","starred_url":"https://api.github.com/users/Automattic/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Automattic/subscriptions","organizations_url":"https://api.github.com/users/Automattic/orgs","repos_url":"https://api.github.com/users/Automattic/repos","events_url":"https://api.github.com/users/Automattic/events{/privacy}","received_events_url":"https://api.github.com/users/Automattic/received_events","type":"Organization","site_admin":false},"repo":{"id":45936895,"name":"wp-calypso","full_name":"Automattic/wp-calypso","owner":{"login":"Automattic","id":887802,"avatar_url":"https://avatars.githubusercontent.com/u/887802?v=3","gravatar_id":"","url":"https://api.github.com/users/Automattic","html_url":"https://github.com/Automattic","followers_url":"https://api.github.com/users/Automattic/followers","following_url":"https://api.github.com/users/Automattic/following{/other_user}","gists_url":"https://api.github.com/users/Automattic/gists{/gist_id}","starred_url":"https://api.github.com/users/Automattic/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Automattic/subscriptions","organizations_url":"https://api.github.com/users/Automattic/orgs","repos_url":"https://api.github.com/users/Automattic/repos","events_url":"https://api.github.com/users/Automattic/events{/privacy}","received_events_url":"https://api.github.com/users/Automattic/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/Automattic/wp-calypso","description":"The new JavaScript- and API-powered WordPress.com","fork":false,"url":"https://api.github.com/repos/Automattic/wp-calypso","forks_url":"https://api.github.com/repos/Automattic/wp-calypso/forks","keys_url":"https://api.github.com/repos/Automattic/wp-calypso/keys{/key_id}","collaborators_url":"https://api.github.com/repos/Automattic/wp-calypso/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/Automattic/wp-calypso/teams","hooks_url":"https://api.github.com/repos/Automattic/wp-calypso/hooks","issue_events_url":"https://api.github.com/repos/Automattic/wp-calypso/issues/events{/number}","events_url":"https://api.github.com/repos/Automattic/wp-calypso/events","assignees_url":"https://api.github.com/repos/Automattic/wp-calypso/assignees{/user}","branches_url":"https://api.github.com/repos/Automattic/wp-calypso/branches{/branch}","tags_url":"https://api.github.com/repos/Automattic/wp-calypso/tags","blobs_url":"https://api.github.com/repos/Automattic/wp-calypso/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/Automattic/wp-calypso/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/Automattic/wp-calypso/git/refs{/sha}","trees_url":"https://api.github.com/repos/Automattic/wp-calypso/git/trees{/sha}","statuses_url":"https://api.github.com/repos/Automattic/wp-calypso/statuses/{sha}","languages_url":"https://api.github.com/repos/Automattic/wp-calypso/languages","stargazers_url":"https://api.github.com/repos/Automattic/wp-calypso/stargazers","contributors_url":"https://api.github.com/repos/Automattic/wp-calypso/contributors","subscribers_url":"https://api.github.com/repos/Automattic/wp-calypso/subscribers","subscription_url":"https://api.github.com/repos/Automattic/wp-calypso/subscription","commits_url":"https://api.github.com/repos/Automattic/wp-calypso/commits{/sha}","git_commits_url":"https://api.github.com/repos/Automattic/wp-calypso/git/commits{/sha}","comments_url":"https://api.github.com/repos/Automattic/wp-calypso/comments{/number}","issue_comment_url":"https://api.github.com/repos/Automattic/wp-calypso/issues/comments{/number}","contents_url":"https://api.github.com/repos/Automattic/wp-calypso/contents/{+path}","compare_url":"https://api.github.com/repos/Automattic/wp-calypso/compare/{base}...{head}","merges_url":"https://api.github.com/repos/Automattic/wp-calypso/merges","archive_url":"https://api.github.com/repos/Automattic/wp-calypso/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/Automattic/wp-calypso/downloads","issues_url":"https://api.github.com/repos/Automattic/wp-calypso/issues{/number}","pulls_url":"https://api.github.com/repos/Automattic/wp-calypso/pulls{/number}","milestones_url":"https://api.github.com/repos/Automattic/wp-calypso/milestones{/number}","notifications_url":"https://api.github.com/repos/Automattic/wp-calypso/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/Automattic/wp-calypso/labels{/name}","releases_url":"https://api.github.com/repos/Automattic/wp-calypso/releases{/id}","deployments_url":"https://api.github.com/repos/Automattic/wp-calypso/deployments","created_at":"2015-11-10T20:00:16Z","updated_at":"2016-02-24T14:48:30Z","pushed_at":"2016-02-24T15:58:11Z","git_url":"git://github.com/Automattic/wp-calypso.git","ssh_url":"git@github.com:Automattic/wp-calypso.git","clone_url":"https://github.com/Automattic/wp-calypso.git","svn_url":"https://github.com/Automattic/wp-calypso","homepage":"https://developer.wordpress.com/calypso/","size":55872,"stargazers_count":6857,"watchers_count":6857,"language":"JavaScript","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":792,"mirror_url":null,"open_issues_count":1251,"forks":792,"open_issues":1251,"watchers":6857,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/Automattic/wp-calypso/pulls/3143"},"html":{"href":"https://github.com/Automattic/wp-calypso/pull/3143"},"issue":{"href":"https://api.github.com/repos/Automattic/wp-calypso/issues/3143"},"comments":{"href":"https://api.github.com/repos/Automattic/wp-calypso/issues/3143/comments"},"review_comments":{"href":"https://api.github.com/repos/Automattic/wp-calypso/pulls/3143/comments"},"review_comment":{"href":"https://api.github.com/repos/Automattic/wp-calypso/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/Automattic/wp-calypso/pulls/3143/commits"},"statuses":{"href":"https://api.github.com/repos/Automattic/wp-calypso/statuses/88b2d5e3460cf694cf5caf28d5fd73fbb1a6bbf6"}}}}