From 5a2c5499271f6ffdf08bb249f4022d78c181ccd6 Mon Sep 17 00:00:00 2001 From: Ben Halpern Date: Fri, 13 Jul 2018 20:05:47 -0400 Subject: [PATCH] Add reactions to /connect content (#585) --- app/assets/stylesheets/chat.scss | 53 +++++++++ app/controllers/reactions_controller.rb | 2 +- app/javascript/chat/article.jsx | 138 ++++++++++++++++++++++++ app/javascript/chat/content.jsx | 22 +--- 4 files changed, 194 insertions(+), 21 deletions(-) create mode 100644 app/javascript/chat/article.jsx diff --git a/app/assets/stylesheets/chat.scss b/app/assets/stylesheets/chat.scss index ec0371f24..adb63e88f 100644 --- a/app/assets/stylesheets/chat.scss +++ b/app/assets/stylesheets/chat.scss @@ -377,6 +377,59 @@ top:-4px; } +.activechatchannel__activeArticle{ + position: absolute; + top: 38px; left: 0; right: 0; bottom: 0; + border-top: 1px solid $light-medium-gray !important; +} + +.chat .activechatchannel__activeArticle .container { + position: absolute; + top: 0px; left: 0; right: 0; bottom: 30px; + overflow-y: scroll; + border-radius: 0px; + margin-top: 0px !important; + pre{ + width:97%; + margin-left:-3%; + padding-left:4%; + padding-right:7%; + } +} + +.activechatchannel__activeArticleActions{ + position:absolute; + bottom:0; + left:0px; + right:0px; + padding: 19px; + background: white; + z-index: 20; + border-top: 1px solid $light-medium-gray; + button { + border: 1px solid $light-medium-gray; + padding: 3px 20px; + border-radius: 3px; + margin-right: 15px; + &.active { + border: 2px solid $black; + padding: 2px 19px; + background-color:$green; + &.unicorn-reaction-button{ + background-color:$purple; + } + &.readinglist-reaction-button{ + background: lighten($bold-blue, 32%); + } + } + img { + width: 22px; + height: 22px; + } + } +} + + .activecontent__githubrepo{ } diff --git a/app/controllers/reactions_controller.rb b/app/controllers/reactions_controller.rb index eb81f827e..ad85a697c 100644 --- a/app/controllers/reactions_controller.rb +++ b/app/controllers/reactions_controller.rb @@ -60,7 +60,7 @@ class ReactionsController < ApplicationController ) @result = "create" end - render json: { result: @result } + render json: { result: @result, category: params[:category] || "like" } end def cached_user_positive_reactions(user) diff --git a/app/javascript/chat/article.jsx b/app/javascript/chat/article.jsx new file mode 100644 index 000000000..9fd04693d --- /dev/null +++ b/app/javascript/chat/article.jsx @@ -0,0 +1,138 @@ + + +import { h, Component } from 'preact'; +import heartImage from 'images/emoji/emoji-one-heart.png'; +import unicornImage from 'images/emoji/emoji-one-unicorn.png'; +import bookmarkImage from 'images/emoji/emoji-one-bookmark.png'; + +export default class Article extends Component { + constructor(props) { + super(props); + this.state = { + reactionCounts: [], + userReactions: [], + optimisticUserReaction: null, + } + + } + + componentDidMount() { + fetch("/reactions?article_id="+this.props.resource.id, { + Accept: 'application/json', + 'Content-Type': 'application/json', + credentials: 'same-origin', + }) + .then(response => response.json()) + .then(this.displayReactions) + .catch(this.displayReactionsFailure); + } + + displayReactions = response => { + this.setState({userReactions:response.reactions}) + } + + displayReactionsFailure = response => { + console.log(response) + } + + handleNewReactionResponse = (response) => { + let oldUserReactions = this.state.userReactions; + let foundReactions = oldUserReactions.filter(obj => { + return obj.category === response.category + }) + if (foundReactions.length === 0 && response.result === 'create') { + oldUserReactions.push({category: response.category}) + } else { + oldUserReactions = oldUserReactions.filter(obj => { + return obj.category != response.category + }) + } + this.setState({userReactions: oldUserReactions, optimisticUserReaction: null}) + } + + handleNewReactionFailure = response => { + console.log(response) + } + + handleReactionClick = e => { + e.preventDefault(); + const target = e.target; + console.log(target.dataset.category) + this.setState({optimisticUserReaction: target.dataset.category }) + const article = this.props.resource + fetch('/reactions', { + method: 'POST', + headers: { + Accept: 'application/json', + 'X-CSRF-Token': window.csrfToken, + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + reactable_type: 'Article', + reactable_id: article.id, + category: target.dataset.category, + }), + credentials: 'same-origin', + }) + .then(response => response.json()) + .then(this.handleNewReactionResponse) + .catch(this.handleNewReactionFailure); + + } + + render() { + const article = this.props.resource; + let heartReactedClass = '' + let unicornReactedClass = '' + let bookmarkReactedClass = '' + const state = this.state; + state.userReactions.forEach((reaction) => { + if (reaction.category === 'like' || state.optimisticUserReaction === 'like') { + heartReactedClass = 'active' + } + if (reaction.category === 'unicorn' || state.optimisticUserReaction === 'unicorn') { + unicornReactedClass = 'active' + } + if (reaction.category === 'readinglist' || state.optimisticUserReaction === 'readinglist') { + bookmarkReactedClass = 'active' + } + }); + let coverImage = ''; + if (article.cover_image) { + coverImage =
+ } + return ( +
+
+ {coverImage} +
+

{article.title}

+

+ + {article.user.username}/ + {article.user.name} + +

+
+
+
+
+
+
+ + + +
+
) + } +} + diff --git a/app/javascript/chat/content.jsx b/app/javascript/chat/content.jsx index 5c807e9c9..a7dedafeb 100644 --- a/app/javascript/chat/content.jsx +++ b/app/javascript/chat/content.jsx @@ -4,6 +4,7 @@ import CodeEditor from './codeEditor'; import GithubRepo from './githubRepo'; import ChannelDetails from './channelDetails'; import UserDetails from './userDetails'; +import Article from './article'; export default class Content extends Component { static propTypes = { @@ -48,26 +49,7 @@ function display(props) { } else if (props.resource.type_of === "user") { return } else if (props.resource.type_of === "article") { - let coverImage = ''; - if (props.resource.cover_image) { - coverImage =
- } - return ( -
- {coverImage} -
-

{props.resource.title}

-

- - {props.resource.user.username}/ - {props.resource.user.name} - -

-
-
-
-
-
) + return
} else if (props.resource.type_of === "github") { return