From b440e539ea7d138704daed7bda854a7905a62b65 Mon Sep 17 00:00:00 2001 From: Sarthak <7lovesharma7@gmail.com> Date: Mon, 9 Dec 2019 15:15:15 +0530 Subject: [PATCH 01/10] =?UTF-8?q?Feature=20=F0=9F=9A=80=20:=20Ability=20to?= =?UTF-8?q?=20delete=20messages=20in=20chat=20channels?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Sending message ID to frontend - Deleting Message - Use pusher to delete message realtime --- app/assets/stylesheets/chat.scss | 65 +++++++++++++ app/controllers/messages_controller.rb | 41 +++++++- .../__snapshots__/chat.test.jsx.snap | 34 +++++++ .../__snapshots__/message.test.jsx.snap | 56 +++++++---- app/javascript/chat/actions.js | 20 ++++ app/javascript/chat/chat.jsx | 95 ++++++++++++++++++- app/javascript/chat/message.jsx | 60 ++++++++---- .../src/utils/getUnopenedChannels.jsx | 15 +-- app/javascript/src/utils/pusher.js | 1 + app/policies/message_policy.rb | 10 ++ app/views/chat_channels/show.json.jbuilder | 1 + config/routes.rb | 1 + 12 files changed, 352 insertions(+), 47 deletions(-) diff --git a/app/assets/stylesheets/chat.scss b/app/assets/stylesheets/chat.scss index 30a71a795bc07..c7685796e9f66 100644 --- a/app/assets/stylesheets/chat.scss +++ b/app/assets/stylesheets/chat.scss @@ -1226,4 +1226,69 @@ .chatchanneljumpback__hide{ display: none; +} + +.message__info__actions{ + display: grid; + grid-template-columns: 4fr 1fr; +} +.message__actions{ + display: none; + justify-content: end; +} +.message__actions span { + margin-left: 7px; + font-size: 13px; + font-weight: bold; + cursor: pointer; +} +.chatmessage:hover .message__actions{ + display: flex; +} + +.message__delete__modal{ + position: fixed; + background-color: rgba(255, 255, 255, 0.25); + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 999; + display: block; + transition: all 0.3s; +} +.modal__content{ + width: 400px; + position: absolute; + top: 50%; + left: 50%; + -webkit-transform: translate(-50%, -50%); + transform: translate(-50%, -50%); + padding: 1em; + border: 2px solid; + background: var(--theme-background, #f5f6f7); + color: var(--theme-color, $black); +} +.modal__content h3{ + margin: 20px 0; + text-align: center; +} +.delete__action__buttons{ + display: flex; + justify-content: space-around; + margin: 40px 0px 20px 0; +} + +.delete__action__buttons div { + border: 2px solid var(--theme-color, $black); + padding: 5px 20px; + cursor: pointer; + user-select: none; +} +.message__delete__modal__hide{ + display: none; +} +.message__delete__button{ + background: #ff0000; + color: white; } \ No newline at end of file diff --git a/app/controllers/messages_controller.rb b/app/controllers/messages_controller.rb index 821a338947f61..7a91fe9f47d5e 100644 --- a/app/controllers/messages_controller.rb +++ b/app/controllers/messages_controller.rb @@ -1,14 +1,16 @@ class MessagesController < ApplicationController - before_action :authenticate_user! + before_action :set_message, only: %i[destroy] + before_action :authenticate_user!, only: %i[create] def create @message = Message.new(message_params) @message.user_id = session_current_user_id + @temp_message_id = (0...20).map { ("a".."z").to_a[rand(8)] }.join authorize @message if @message.valid? begin - message_json = create_pusher_payload(@message) + message_json = create_pusher_payload(@message, @temp_message_id) Pusher.trigger(@message.chat_channel.pusher_channels, "message-created", message_json) rescue Pusher::Error => e logger.info "PUSHER ERROR: #{e.message}" @@ -16,7 +18,32 @@ def create end if @message.save - render json: { status: "success", message: "Message created" }, status: :created + render json: { status: "success", message: { temp_id: @temp_message_id, id: @message.id } }, status: :created + else + render json: { + status: "error", + message: { + chat_channel_id: @message.chat_channel_id, + message: @message.errors.full_messages, + type: "error" + } + }, status: :unauthorized + end + end + + def destroy + authorize @message + + if @message.valid? + begin + Pusher.trigger(@message.chat_channel.pusher_channels, "message-deleted", @message.to_json) + rescue Pusher::Error => e + logger.info "PUSHER ERROR: #{e.message}" + end + end + + if @message.destroy + render json: { status: "success", message: "Message was deleted" } else render json: { status: "error", @@ -31,8 +58,9 @@ def create private - def create_pusher_payload(new_message) + def create_pusher_payload(new_message, temp_id) { + temp_id: temp_id, user_id: new_message.user.id, chat_channel_id: new_message.chat_channel.id, chat_channel_adjusted_slug: new_message.chat_channel.adjusted_slug(current_user, "sender"), @@ -49,6 +77,11 @@ def message_params params.require(:message).permit(:message_markdown, :user_id, :chat_channel_id) end + def set_message + logger.info "PUSHER ERROR: #{params[:id]}" + @message = Message.find(params[:id]) + end + def user_not_authorized respond_to do |format| format.json do diff --git a/app/javascript/chat/__tests__/__snapshots__/chat.test.jsx.snap b/app/javascript/chat/__tests__/__snapshots__/chat.test.jsx.snap index cd6c4704c444a..22ee18eec3f04 100644 --- a/app/javascript/chat/__tests__/__snapshots__/chat.test.jsx.snap +++ b/app/javascript/chat/__tests__/__snapshots__/chat.test.jsx.snap @@ -198,6 +198,40 @@ exports[` should load chat 1`] = ` Scroll to Bottom +
+ +
diff --git a/app/javascript/chat/__tests__/__snapshots__/message.test.jsx.snap b/app/javascript/chat/__tests__/__snapshots__/message.test.jsx.snap index d92a9e227d405..91f840a043cc4 100644 --- a/app/javascript/chat/__tests__/__snapshots__/message.test.jsx.snap +++ b/app/javascript/chat/__tests__/__snapshots__/message.test.jsx.snap @@ -26,25 +26,47 @@ exports[` should render and test snapshot 1`] = ` class="chatmessage__body" role="presentation" > - - - asdf - - - + + + asdf + + + +
+
+ + Delete + + + Edit + +
+
diff --git a/app/javascript/chat/actions.js b/app/javascript/chat/actions.js index 7e516d8ff658a..99bffbacc39a6 100644 --- a/app/javascript/chat/actions.js +++ b/app/javascript/chat/actions.js @@ -178,3 +178,23 @@ export function sendChannelInviteAction(id, action, successCb, failureCb) { .then(successCb) .catch(failureCb); } + +export function deleteMessage(messageId, successCb, failureCb) { + fetch(`/messages/${messageId}`, { + method: 'DELETE', + headers: { + Accept: 'application/json', + 'X-CSRF-Token': window.csrfToken, + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + message: { + user_id: window.currentUser.id, + }, + }), + credentials: 'same-origin', + }) + .then(response => response.json()) + .then(successCb) + .catch(failureCb); +} diff --git a/app/javascript/chat/chat.jsx b/app/javascript/chat/chat.jsx index 897d0650cac5b..a366ebac67c35 100644 --- a/app/javascript/chat/chat.jsx +++ b/app/javascript/chat/chat.jsx @@ -10,6 +10,7 @@ import { getContent, getChannelInvites, sendChannelInviteAction, + deleteMessage, } from './actions'; import { hideMessages, scrollToBottom, setupObserver } from './util'; import Alert from './alert'; @@ -61,6 +62,8 @@ export default class Chat extends Component { soundOn: true, videoOn: true, messageOffset: 0, + showDeleteModal: false, + messageDeleteId: null, allMessagesLoaded: false, currentMessageLocation: 0, }; @@ -166,6 +169,7 @@ export default class Chat extends Component { setupPusher(pusherKey, { channelId: channelName, messageCreated: this.receiveNewMessage, + messageDeleted: this.removeMessage, channelCleared: this.clearChannel, redactUserMessages: this.redactUserMessages, channelError: this.channelError, @@ -302,10 +306,24 @@ export default class Chat extends Component { })); }; + removeMessage = message => { + const { activeChannelId } = this.state; + this.setState(prevState => ({ + messages: { + [activeChannelId]: { + ...prevState.messages[activeChannelId].filter( + oldmessage => oldmessage.id !== message.id, + ), + }, + }, + })); + }; + receiveNewMessage = message => { const { messages, activeChannelId, scrolled, chatChannels } = this.state; const receivedChatChannelId = message.chat_channel_id; let newMessages = []; + if (messages[receivedChatChannelId]) { newMessages = messages[receivedChatChannelId].slice(); newMessages.push(message); @@ -524,8 +542,25 @@ export default class Chat extends Component { } }; + triggerDeleteMessage = e => { + this.setState({ messageDeleteId: e.target.dataset.content }); + this.setState({ showDeleteModal: true }); + }; + handleSuccess = response => { - if (response.status === 'error') { + const { activeChannelId } = this.state; + if (response.status === 'success') { + if (response.message.temp_id) { + this.setState(({ messages }) => { + const newMessages = messages; + const foundIndex = messages[activeChannelId].findIndex( + message => message.temp_id === response.message.temp_id, + ); + newMessages[activeChannelId][foundIndex].id = response.message.id; + return { messages: newMessages }; + }); + } + } else if (response.status === 'error') { this.receiveNewMessage(response.message); } }; @@ -717,6 +752,7 @@ export default class Chat extends Component { } return messages[activeChannelId].map(message => ( )); }; @@ -951,6 +988,7 @@ export default class Chat extends Component { Scroll to Bottom
+ {this.renderDeleteModal()}
@@ -974,6 +1012,61 @@ export default class Chat extends Component { ); }; + renderDeleteModal = () => { + const { showDeleteModal } = this.state; + return ( +
+
+

Are you sure, you want to delete this message ?

+ +
+
{ + if (e.keyCode === 13) this.handleCloseDeleteModal(); + }} + > + {' '} + Cancel +
+
{ + if (e.keyCode === 13) this.handleMessageDelete(); + }} + > + {' '} + Delete +
+
+
+
+ ); + }; + + handleCloseDeleteModal = () => { + this.setState({ showDeleteModal: false, messageDeleteId: null }); + }; + + handleMessageDelete = () => { + const { messageDeleteId } = this.state; + deleteMessage(messageDeleteId); + this.setState({ showDeleteModal: false }); + }; + renderChannelHeaderInner = () => { const { activeChannel, activeChannelId } = this.state; if (activeChannel.channel_type === 'direct') { diff --git a/app/javascript/chat/message.jsx b/app/javascript/chat/message.jsx index 4ae3087f87ddf..345bf073b1be2 100644 --- a/app/javascript/chat/message.jsx +++ b/app/javascript/chat/message.jsx @@ -4,6 +4,7 @@ import { adjustTimestamp } from './util'; import ErrorMessage from './messages/errorMessage'; const Message = ({ + id, user, userID, message, @@ -12,6 +13,7 @@ const Message = ({ timestamp, profileImageUrl, onContentTrigger, + onDeleteMessageTrigger, }) => { const spanStyle = { color }; @@ -52,25 +54,43 @@ const Message = ({ className="chatmessage__body" onClick={onContentTrigger} > - - - {user} - - - {timestamp ? ( - - {`${adjustTimestamp(timestamp)}`} - - ) : ( - - )} +
+
+ + + {user} + + + {timestamp ? ( + + {`${adjustTimestamp(timestamp)}`} + + ) : ( + + )} +
+
+ { + if (e.keyCode === 13) onDeleteMessageTrigger(); + }} + > + Delete + + Edit +
+
{messageArea}
@@ -78,6 +98,7 @@ const Message = ({ }; Message.propTypes = { + id: PropTypes.number.isRequired, user: PropTypes.string.isRequired, userID: PropTypes.number.isRequired, color: PropTypes.string.isRequired, @@ -86,6 +107,7 @@ Message.propTypes = { timestamp: PropTypes.string, profileImageUrl: PropTypes.string, onContentTrigger: PropTypes.func.isRequired, + onDeleteMessageTrigger: PropTypes.func.isRequired, }; Message.defaultProps = { diff --git a/app/javascript/src/utils/getUnopenedChannels.jsx b/app/javascript/src/utils/getUnopenedChannels.jsx index 312da141ed44f..bca546e7ba7a5 100644 --- a/app/javascript/src/utils/getUnopenedChannels.jsx +++ b/app/javascript/src/utils/getUnopenedChannels.jsx @@ -3,16 +3,16 @@ import PropTypes from 'prop-types'; import setupPusher from './pusher'; class UnopenedChannelNotice extends Component { - static defaultProps = { - unopenedChannels: undefined, - pusherKey: undefined, - }; - propTypes = { unopenedChannels: PropTypes.Object, pusherKey: PropTypes.Object, }; + static defaultProps = { + unopenedChannels: undefined, + pusherKey: undefined, + }; + constructor(props) { super(props); const { unopenedChannels } = this.props; @@ -28,6 +28,7 @@ class UnopenedChannelNotice extends Component { setupPusher(pusherKey, { channelId: `private-message-notifications-${window.currentUser.id}`, messageCreated: this.receiveNewMessage, + messageDeleted: this.removeMessage, }); const component = this; document.getElementById('connect-link').onclick = () => { @@ -37,6 +38,8 @@ class UnopenedChannelNotice extends Component { }; } + removeMessage = () => {}; + receiveNewMessage = e => { if (window.location.pathname.startsWith('/connect')) { return; @@ -118,7 +121,7 @@ class UnopenedChannelNotice extends Component { padding: '19px 5px 14px', }} > - New Message from + New Message from {' '} {channels} diff --git a/app/javascript/src/utils/pusher.js b/app/javascript/src/utils/pusher.js index 4f905ed26ce88..d1ad57e474e7c 100644 --- a/app/javascript/src/utils/pusher.js +++ b/app/javascript/src/utils/pusher.js @@ -19,6 +19,7 @@ export default function setupPusher(key, callbackObjects) { const channel = pusher.subscribe(callbackObjects.channelId.toString()); channel.bind('message-created', callbackObjects.messageCreated); + channel.bind('message-deleted', callbackObjects.messageDeleted); channel.bind('channel-cleared', callbackObjects.channelCleared); channel.bind('user-banned', callbackObjects.redactUserMessages); channel.bind('client-livecode', callbackObjects.liveCoding); diff --git a/app/policies/message_policy.rb b/app/policies/message_policy.rb index 3f38f236cc687..ae954e69ce6e2 100644 --- a/app/policies/message_policy.rb +++ b/app/policies/message_policy.rb @@ -2,4 +2,14 @@ class MessagePolicy < ApplicationPolicy def create? !user_is_banned? end + + def destroy? + user_is_sender? + end + + private + + def user_is_sender? + record.user_id == user.id + end end diff --git a/app/views/chat_channels/show.json.jbuilder b/app/views/chat_channels/show.json.jbuilder index 1c5176ff4d32b..2a22cc35e8ce1 100644 --- a/app/views/chat_channels/show.json.jbuilder +++ b/app/views/chat_channels/show.json.jbuilder @@ -1,4 +1,5 @@ json.messages @chat_messages.reverse do |message| + json.id message.id json.user_id message.user_id json.username message.user.username json.profile_image_url ProfileImage.new(message.user).get(90) diff --git a/config/routes.rb b/config/routes.rb index 66e29b5314c4b..a39f86580eede 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -218,6 +218,7 @@ get "/connect/:slug" => "chat_channels#index" post "/chat_channels/create_chat" => "chat_channels#create_chat" post "/chat_channels/block_chat" => "chat_channels#block_chat" + delete "/messages/:id" => "messages#destroy" get "/live/:username" => "twitch_live_streams#show" post "/pusher/auth" => "pusher#auth" From e6f8d2922f76d5375d7005290dc78db91872272d Mon Sep 17 00:00:00 2001 From: Sarthak <7lovesharma7@gmail.com> Date: Mon, 9 Dec 2019 16:58:03 +0530 Subject: [PATCH 02/10] =?UTF-8?q?Minor=20Bug=20=F0=9F=90=9E:=20Show=20mess?= =?UTF-8?q?age=20action=20only=20for=20current=20user?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - User can delete or edit their own messages --- app/javascript/chat/message.jsx | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/app/javascript/chat/message.jsx b/app/javascript/chat/message.jsx index 345bf073b1be2..08c2638cbe578 100644 --- a/app/javascript/chat/message.jsx +++ b/app/javascript/chat/message.jsx @@ -76,20 +76,24 @@ const Message = ({ )} -
- { - if (e.keyCode === 13) onDeleteMessageTrigger(); - }} - > - Delete - - Edit -
+ {userID === window.currentUser.id ? ( +
+ { + if (e.keyCode === 13) onDeleteMessageTrigger(); + }} + > + Delete + + Edit +
+ ) : ( + ' ' + )}
{messageArea}
From e796764564c957370c4c83b395929c0a4e2b8c82 Mon Sep 17 00:00:00 2001 From: Sarthak <7lovesharma7@gmail.com> Date: Mon, 9 Dec 2019 17:10:11 +0530 Subject: [PATCH 03/10] Test cases added --- app/javascript/chat/chat.jsx | 1 + app/javascript/chat/message.jsx | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/app/javascript/chat/chat.jsx b/app/javascript/chat/chat.jsx index a366ebac67c35..6f70d1f0230f6 100644 --- a/app/javascript/chat/chat.jsx +++ b/app/javascript/chat/chat.jsx @@ -752,6 +752,7 @@ export default class Chat extends Component { } return messages[activeChannelId].map(message => ( )} - {userID === window.currentUser.id ? ( + {userID === currentUserId ? (
Date: Mon, 9 Dec 2019 19:00:30 +0530 Subject: [PATCH 04/10] =?UTF-8?q?Bug=20=F0=9F=90=9E:=20Update=20message=20?= =?UTF-8?q?id=20for=20receiver?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Message id was not sent to receiver by pusher --- app/assets/stylesheets/chat.scss | 777 +++++++++++++------------ app/controllers/messages_controller.rb | 13 +- app/javascript/chat/chat.jsx | 13 +- 3 files changed, 422 insertions(+), 381 deletions(-) diff --git a/app/assets/stylesheets/chat.scss b/app/assets/stylesheets/chat.scss index c7685796e9f66..f665c5492fb88 100644 --- a/app/assets/stylesheets/chat.scss +++ b/app/assets/stylesheets/chat.scss @@ -3,18 +3,22 @@ @import 'mixins'; // High level classes -.chat-page-wrapper{ +.chat-page-wrapper { margin: 85px auto 0px; width: 96%; max-width: calc(1440px + 10vw); } -.live-chat{ + +.live-chat { height: calc(100vh - 80px); - &.live-chat--iossafari{ + + &.live-chat--iossafari { height: calc(100vh - 190px); } + overflow-y: hidden; - @media screen and ( min-width: 400px ){ + + @media screen and (min-width: 400px) { height: calc(100vh - 80px); } } @@ -23,24 +27,28 @@ .chat { display: flex; height: calc(100vh - 91px); - &.chat--iossafari{ + + &.chat--iossafari { height: calc(100vh - 201px); } + padding-right: 3px; - @media screen and ( min-width: 400px ){ + + @media screen and (min-width: 400px) { height: calc(100vh - 91px); } } -.chat--expanded{ +.chat--expanded { min-width: 500px; width: calc(100%-3px); - @media screen and ( min-width: 500px ){ + + @media screen and (min-width: 500px) { min-width: 300px; } } -.chat__notificationsbutton{ +.chat__notificationsbutton { border: 0px; font-size: 12px; padding: 15px 0px; @@ -51,14 +59,13 @@ border-radius: 3px; border: 1px solid darken($green, 30%); box-shadow: 3px 3px 0px darken($green, 30%); - background: lighten($green,30%); + background: lighten($green, 30%); margin-bottom: 5px; - &:hover{ - @include themeable( - background, - theme-container-background-hover, - lighten($green, 20%) - ); + + &:hover { + @include themeable(background, + theme-container-background-hover, + lighten($green, 20%)); } } @@ -70,33 +77,41 @@ overflow: hidden; } -.chat__channels--expanded{ +.chat__channels--expanded { width: 160px; min-width: 160px; - @media screen and ( min-width: 1000px ){ + + @media screen and (min-width: 1000px) { width: 200px; min-width: 200px; - .chat__channelstogglebutt{ + + .chat__channelstogglebutt { display: none; } } - @media screen and ( min-width: 1300px ){ + + @media screen and (min-width: 1300px) { width: calc(280px + 3vw); min-width: calc(280px); } - &.chat__channels--placeholder{ + + &.chat__channels--placeholder { .chat__channelstogglebutt--placeholderunexpanded { display: none; } - @media screen and ( max-width: 599px ){ + + @media screen and (max-width: 599px) { width: 45px; min-width: 45px; - input{ + + input { display: none; } - .chat__channelstogglebutt{ + + .chat__channelstogglebutt { display: none; } + .chat__channelstogglebutt--placeholderunexpanded { display: block; width: 100%; @@ -105,7 +120,7 @@ } } -.chat__channelstogglebutt{ +.chat__channelstogglebutt { width: 26px; height: 38px; background: transparent; @@ -123,27 +138,24 @@ padding: 8px; font-size: 14px; -webkit-appearance: none; - @include themeable( - background, - theme-container-background, - white - ); - @include themeable( - color, - theme-color, - $black - ); - @media screen and ( min-width: 1000px ){ + @include themeable(background, + theme-container-background, + white); + @include themeable(color, + theme-color, + $black); + + @media screen and (min-width: 1000px) { width: calc(100% - 13px); } } -.chat__channeltypefilter{ +.chat__channeltypefilter { padding: 4px 0px 0px; width: calc(100% - 13px); } -.chat__channeltypefilterbutton{ +.chat__channeltypefilterbutton { background: transparent; border: 0px; font-size: 15px; @@ -153,7 +165,8 @@ width: 100%; box-sizing: border-box; border-bottom: 1px solid darken($light-medium-gray, 5%); - @media screen and ( min-width: 1300px ){ + + @media screen and (min-width: 1300px) { width: 33%; display: inline-block; border-top-left-radius: 3px; @@ -162,48 +175,44 @@ } .chat__channeltypefilterbutton--active { - @include themeable( - color, - theme-anchor-color, - darken($sky-blue, 5%) - ); - @media screen and ( min-width: 1300px ){ + @include themeable(color, + theme-anchor-color, + darken($sky-blue, 5%)); + + @media screen and (min-width: 1300px) { border: 1px solid darken($light-medium-gray, 5%); } } .chat__channeltypefilterbutton--inactive { - @include themeable( - color, - theme-secondary-color, + @include themeable(color, + theme-secondary-color, lighten($medium-gray, 5%), ); } -.chat_chatconfig{ +.chat_chatconfig { position: absolute; bottom: 0px; left: 0px; right: 6px; - font-size:12px; + font-size: 12px; padding: 3px 12px; display: inline-block; border-radius: 3px; - @include themeable( - background, - theme-background, - $lightest-gray - ); + @include themeable(background, + theme-background, + $lightest-gray); border: 1px solid $light-medium-gray; font-weight: bold; cursor: default; } -.chat_chatconfig--on{ +.chat_chatconfig--on { color: $green; } -.chat_chatconfig--off{ +.chat_chatconfig--off { color: $red; } @@ -215,15 +224,16 @@ height: inherit; width: calc(100% - 45px); max-width: calc(100% - 45px); - @media screen and ( min-width: 1300px ){ + + @media screen and (min-width: 1300px) { min-width: calc(100% - calc(280px + 3vw)); } } -.chat__videocall{ +.chat__videocall { height: calc(75vw - 60px); width: calc(100% - 60px); - position:fixed; + position: fixed; background: $black; left: 0px; top: 0px; @@ -231,50 +241,61 @@ cursor: grab; border-radius: 12px; z-index: 10000; - @media screen and ( min-width: 640px ){ + + @media screen and (min-width: 640px) { width: 640px; height: 480px; left: 250px; top: 150px; } - &:active{ + + &:active { cursor: grabbing; } - video{ + + video { border-radius: 12px; width: 100%; height: 100%; } - .chat__remotevideoscreen-2{ - video{ + + .chat__remotevideoscreen-2 { + video { margin-top: 20%; - width:45%; + width: 45%; margin-left: 1%; margin-right: 1%; } } - .chat__remotevideoscreen-3,.chat__remotevideoscreen-4{ - video{ - width:45%; + + .chat__remotevideoscreen-3, + .chat__remotevideoscreen-4 { + video { + width: 45%; margin-top: 1%; margin-left: 1%; margin-right: 1%; } } - .chat__remotevideoscreen-5,.chat__remotevideoscreen-6{ - video{ - width:30%; + + .chat__remotevideoscreen-5, + .chat__remotevideoscreen-6 { + video { + width: 30%; margin-top: 3%; margin-left: 1%; margin-right: 1%; } } - .chat__remotevideoscreen-7,.chat__remotevideoscreen-8{ - video{ - width:24%; + + .chat__remotevideoscreen-7, + .chat__remotevideoscreen-8 { + video { + width: 24%; } } - .chat__localvideoscren{ + + .chat__localvideoscren { height: 120px; width: 160px; position: absolute; @@ -282,56 +303,53 @@ bottom: 15px; border-radius: 5px; box-shadow: 2px 2px 2px 1px rgba(0, 0, 0, 0.4); - video{ + + video { border-radius: 5px } } } -.chatNonChatView{ +.chatNonChatView { position: absolute; left: 0; right: 0; bottom: 0; top: 0; padding: 80px 7%; - z-index:10; - @include themeable( - background, - theme-background, - white - ); + z-index: 10; + @include themeable(background, + theme-background, + white); } -.chatNonChatView_exitbutton{ +.chatNonChatView_exitbutton { position: absolute; left: 4%; top: 60px; - font-size:25px; + font-size: 25px; background: transparent; - border:0px; - font-size:1.8em; - @include themeable( - color, - theme-color, - $black - ); + border: 0px; + font-size: 1.8em; + @include themeable(color, + theme-color, + $black); } -.chatNonChatView_contentblock{ +.chatNonChatView_contentblock { border: 1px solid $medium-gray; padding: 20px; margin-bottom: 10px; max-width: 900px; - @include themeable( - box-shadow, - theme-container-box-shadow, - $bold-shadow - ); - h2{ - margin:5px auto 5px; + @include themeable(box-shadow, + theme-container-box-shadow, + $bold-shadow); + + h2 { + margin: 5px auto 5px; } - button{ + + button { font-size: 1.6em; background: white; border: 4px solid $black; @@ -341,12 +359,12 @@ } } -.chat__videocallexitbutton{ - border: 0px; +.chat__videocallexitbutton { + border: 0px; font-size: 20px; color: $medium-gray; - padding:1px 6px 2px; - border-radius:6px; + padding: 1px 6px 2px; + border-radius: 6px; background: rgba(0, 0, 0, 0.1); position: absolute; left: 3px; @@ -354,16 +372,17 @@ } .chat__videocallcontrolbutton { - border: 0px; + border: 0px; font-size: 14px; color: $light-medium-gray; - padding:3px 0px 4px; - border-radius:6px; + padding: 3px 0px 4px; + border-radius: 6px; background: rgba(0, 0, 0, 0.7); position: absolute; bottom: 10px; left: 10px; width: 125px; + &.chat__videocallcontrolbutton--videoonoff { left: 140px; } @@ -383,62 +402,50 @@ display: flex; flex-direction: row; justify-content: space-between; - @include themeable( - border, - theme-container-border, - 1px solid $outline-color - ); - @include themeable( - box-shadow, - theme-container-box-shadow, - $bold-shadow - ); - @include themeable( - background, - theme-container-background, - white - ); + @include themeable(border, + theme-container-border, + 1px solid $outline-color); + @include themeable(box-shadow, + theme-container-box-shadow, + $bold-shadow); + @include themeable(background, + theme-container-background, + white); flex-flow: row nowrap; } -.activechatchannel__conversation{ - @include themeable( - border-top, - theme-container-border, - 1px solid $outline-color - ); +.activechatchannel__conversation { + @include themeable(border-top, + theme-container-border, + 1px solid $outline-color); order: 1; display: flex; flex-direction: column; flex: 0 1 auto; width: 100%; min-width: 55%; - @media screen and ( max-width: 426px ) { + + @media screen and (max-width: 426px) { overflow-x: hidden; } } -.activechatchannel__header{ - @include themeable( - border-bottom, - theme-container-border, - 1px solid $light-medium-gray - ); +.activechatchannel__header { + @include themeable(border-bottom, + theme-container-border, + 1px solid $light-medium-gray); padding: 10px 12px; font-weight: bold; - font-size:14px; - @include themeable( - background, - theme-container-accent-background, - $light-gray - ); + font-size: 14px; + @include themeable(background, + theme-container-accent-background, + $light-gray); position: relative; + a { - @include themeable( - color, - theme-color, - $black - ); + @include themeable(color, + theme-color, + $black); } .activechatchannel__channelconfig { @@ -447,11 +454,9 @@ right: 15px; top: 10px; cursor: pointer; - @include themeable( - filter, - theme-social-icon-invert, - invert(0) - ); + @include themeable(filter, + theme-social-icon-invert, + invert(0)); img { height: 100%; @@ -493,21 +498,17 @@ } .activechatchannel__form { - @include themeable( - border-top, - theme-container-border, - 1px solid $outline-color - ); - @include themeable( - background, - theme-container-accent-background, - #ededed - ); + @include themeable(border-top, + theme-container-border, + 1px solid $outline-color); + @include themeable(background, + theme-container-accent-background, + #ededed); width: 100%; height: 70px; } -.activechatchannel__incomingcall{ +.activechatchannel__incomingcall { border: 2px solid $green; box-shadow: 6px 6px 0px $green; padding: 20px; @@ -515,11 +516,11 @@ background: lighten($green, 28%); font-size: 28px; font-weight: bold; - cursor:pointer; + cursor: pointer; animation: pulser 0.5s linear infinite; } -.activechatchannel__activecontent{ +.activechatchannel__activecontent { order: 2; flex: 0 1 auto; border: 1px solid $outline-color; @@ -527,110 +528,116 @@ padding: 13px; min-width: 45%; -webkit-overflow-scrolling: touch; - @include themeable( - background, - theme-container-accent-background, - white - ); + @include themeable(background, + theme-container-accent-background, + white); z-index: 200; position: relative; box-sizing: border-box; overflow-y: auto; - max-width:96%; - @media screen and ( max-width: 426px ) { + max-width: 96%; + + @media screen and (max-width: 426px) { padding: 10px; min-width: 50%; width: calc(95% - 45px); position: fixed; height: calc(100% - 90px); } - @media screen and ( min-width: 1000px ){ + + @media screen and (min-width: 1000px) { margin-left: initial; padding: 18px; - max-width:480px; + max-width: 480px; } - @media screen and ( min-width: 1300px ){ + + @media screen and (min-width: 1300px) { margin-left: initial; padding: 18px; - max-width:620px; + max-width: 620px; } } -.live-chat-wrapper .activechatchannel__activecontent{ +.live-chat-wrapper .activechatchannel__activecontent { min-width: 310px; margin-left: -180px; - max-width:96%; + max-width: 96%; padding: 13px; } -.activechatchannel__activecontentexitbutton{ - border: 0px; +.activechatchannel__activecontentexitbutton { + border: 0px; font-size: 30px; - @include themeable( - color, - theme-container-color, - $medium-gray - ); - padding:0px; + @include themeable(color, + theme-container-color, + $medium-gray); + padding: 0px; background: transparent; position: absolute; left: 6px; - top:-3px; + top: -3px; } -.activechatchannel__activeArticle{ +.activechatchannel__activeArticle { position: absolute; - top: 36px; left: 0; right: 0; bottom: 0; + top: 36px; + left: 0; + right: 0; + bottom: 0; border-top: 1px solid $light-medium-gray !important; } -.activechatchannel__activeArticleDetails{ +.activechatchannel__activeArticleDetails { position: absolute; top: -26px; right: 2px; left: 35px; font-family: $monospace; - font-size:0.8em; - a{ - @include themeable( - color, - theme-container-color, - $dark-gray - ); - } - .activechatchannel__activeArticleDetailsPath{ + font-size: 0.8em; + + a { + @include themeable(color, + theme-container-color, + $dark-gray); + } + + .activechatchannel__activeArticleDetailsPath { display: inline-block; max-width: calc(100% - 15px); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } - img{ + + img { height: 1.1em; width: 1.1em; margin-left: 10px; - vertical-align:2px; - opacity:0.86; - &:hover{ - opacity:1; + vertical-align: 2px; + opacity: 0.86; + + &:hover { + opacity: 1; } } } -.activechatchannel__activecontentuserdetails{ - margin-top:20px; +.activechatchannel__activecontentuserdetails { + margin-top: 20px; font-family: $monospace; - font-size:0.9em; - .key{ + font-size: 0.9em; + + .key { color: $medium-gray; margin-bottom: 3px; - font-size:0.9em + font-size: 0.9em } } .userdetails__blockreport { margin: 10px 0; + button { background-color: rgb(255, 0, 0); color: rgb(255, 255, 254); @@ -643,13 +650,15 @@ border-style: initial; border-color: initial; border-image: initial; + &:first-child { margin-left: 0; } } } -.userdetails__reportabuse, .userdetails__blockmsg { +.userdetails__reportabuse, +.userdetails__blockmsg { a { background-color: rgb(255, 0, 0); color: rgb(255, 255, 254); @@ -663,6 +672,7 @@ border-color: initial; border-image: initial; } + .no { background-color: rgb(78, 87, 239); } @@ -670,53 +680,58 @@ .chat .activechatchannel__activeArticle .container { position: absolute; - top: 0px; left: 0; right: 0; bottom: 30px; + 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%; + + pre { + width: 97%; + margin-left: -3%; + padding-left: 4%; + padding-right: 7%; } } -.activechatchannel__activeArticleActions{ - position:absolute; - bottom:0; - left:0px; - right:0px; +.activechatchannel__activeArticleActions { + position: absolute; + bottom: 0; + left: 0px; + right: 0px; padding: 17px; - @include themeable( - background, - theme-container-accent-background, - white - ); + @include themeable(background, + theme-container-accent-background, + white); z-index: 20; border-top: 1px solid $light-medium-gray; border-bottom: 1px solid $light-medium-gray; border-right: 1px solid $light-medium-gray; + button { margin-right: 15px; width: 54px; - @include themeable( - background, - theme-container-background, - #f2f3f5 - ); + @include themeable(background, + theme-container-background, + #f2f3f5); border-radius: 100px !important; border-width: 0; padding: 5px 10px; + &.active { - background-color:$green; - &.unicorn-reaction-button{ - background-color:$purple; + background-color: $green; + + &.unicorn-reaction-button { + background-color: $purple; } - &.readinglist-reaction-button{ + + &.readinglist-reaction-button { background: lighten($bold-blue, 32%); } - } + } + img { width: 22px; height: 22px; @@ -725,24 +740,24 @@ } -.activecontent__githubrepo{ -} +.activecontent__githubrepo {} -.activecontent__githubrepoheader{ +.activecontent__githubrepoheader { padding-left: 30px; padding-bottom: 3px; min-height: 30px; - margin-top:-9px; + margin-top: -9px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; font-size: 0.9em; } -.activecontent__githubrepofiles{ +.activecontent__githubrepofiles { border-bottom: 1px solid $light-medium-gray; - font-size:0.8em; + font-size: 0.8em; overflow: auto; + &.activecontent__githubrepofiles--root { height: 280px; max-height: 40vh; @@ -751,12 +766,13 @@ .activecontent__githubreporeadme { font-size: 0.8em; + img { max-width: 100%; } } -.activecontent__githubrepofilerow{ +.activecontent__githubrepofilerow { padding: 4px 2px; border-top: 1px solid $light-medium-gray; } @@ -781,17 +797,16 @@ width: 99%; } -.chatchanneltabbutton{ +.chatchanneltabbutton { width: 93%; border: none; background: transparent; - padding:3px 0px; + padding: 3px 0px; margin-bottom: -5px 0; - @include themeable( - color, - theme-color, - $black - ); + @include themeable(color, + theme-color, + $black); + &:hover { .chatchanneltab--inactive { border: 1px solid $outline-color; @@ -801,7 +816,7 @@ } .chatchanneltab { - display:inline-block; + display: inline-block; width: 90%; margin-bottom: 5px; background: inherit; @@ -814,18 +829,17 @@ white-space: nowrap; overflow: hidden; text-overflow: ellipsis; - @media screen and ( min-width: 550px ){ + + @media screen and (min-width: 550px) { font-size: 13px; padding: 8px; } } .chatchanneltab--active { - @include themeable( - background, - theme-container-background, - white - ); + @include themeable(background, + theme-container-background, + white); border: 1px solid $dark-purple; box-shadow: 3px 3px 0px $dark-purple; } @@ -840,7 +854,7 @@ animation: small-pulser 0.5s linear infinite; } -.chatchanneltabindicator{ +.chatchanneltabindicator { display: inline-block; min-height: 0.3em; min-width: 0.3em; @@ -853,28 +867,29 @@ vertical-align: calc(-7px - 0.14vw); border: 2px solid transparent; - &.chatchanneltabindicatordirectimage{ + &.chatchanneltabindicatordirectimage { border: 2px solid $light-medium-gray; border-radius: 100px; } } - .chatchanneltabgroupicon{ + .chatchanneltabgroupicon { display: inline-block; margin-left: calc(20px + 0.3vw); } } -.chatchanneltabindicator--new{ +.chatchanneltabindicator--new { background: $yellow; + img.chatchanneltabindicatordirectimage { border: 2px solid $black; } } -.chatchanneltabindicator--phone{ +.chatchanneltabindicator--phone { background: $green; padding: 0px 8px; } @@ -888,7 +903,7 @@ -webkit-overflow-scrolling: touch; } -.chatchannels__channelslistheader{ +.chatchannels__channelslistheader { background: lighten($purple, 5%); border: 1px solid darken($purple, 20%); font-size: 12px; @@ -899,39 +914,40 @@ border-radius: 3px; } -.chatchannels__channelslistfooter{ +.chatchannels__channelslistfooter { font-size: 12px; color: $medium-gray; padding: 10px; - opacity:0.8; + opacity: 0.8; padding-bottom: 70px; } -.chatchannels__config{ +.chatchannels__config { position: absolute; bottom: -14px; left: 0; right: 0; - @include themeable( - background, - theme-background, - $lightest-gray - ); + @include themeable(background, + theme-background, + $lightest-gray); border-top: 1px solid $light-medium-gray; padding: 8px 10px 20px; font-weight: bold; padding-left: 16px; cursor: pointer; - img{ - opacity:0.6; + + img { + opacity: 0.6; } - &:hover{ - .chatchannels__configmenu{ + + &:hover { + .chatchannels__configmenu { display: block; } } } -.chatchannels__configmenu{ + +.chatchannels__configmenu { display: none; position: absolute; bottom: 42px; @@ -942,6 +958,7 @@ background: $lightest-gray; border-top: 1px solid $light-medium-gray; font-size: 13px; + a { color: $dark-gray; display: block; @@ -959,26 +976,22 @@ padding: 3px 10px; display: flex; max-width: 100%; + &:hover { - @include themeable( - background, - theme-container-background-hover, - $light-gray - ); + @include themeable(background, + theme-container-background-hover, + $light-gray); + .chatmessagebody__username--link { - @include themeable( - background, - theme-container-background-hover, - transparent - ); + @include themeable(background, + theme-container-background-hover, + transparent); } } } -.chatmessage__profileimage { - -} +.chatmessage__profileimage {} .chatmessage__body { flex-grow: 1; @@ -993,28 +1006,30 @@ font-size: 11px; color: darken($light-medium-gray, 6%); min-width: 85px; - text-align:right; + text-align: right; padding-left: 4px; vertical-align: 1px; } -.chatmessage__bodytext{ +.chatmessage__bodytext { margin: 3px 1px 4px; overflow-wrap: break-word; word-wrap: break-word; word-break: break-word; - pre{ + + pre { background: #29292e; } + code { background: #29292e; - padding:0.1em 0.3em; - border-radius:2px; + padding: 0.1em 0.3em; + border-radius: 2px; color: #eff0f9; - font-size:0.95em; - vertical-align:0.05em; - max-width:100%; - line-height:1.4em; + font-size: 0.95em; + vertical-align: 0.05em; + max-width: 100%; + line-height: 1.4em; font-family: $monospace; } } @@ -1025,7 +1040,8 @@ margin-right: 6px; border-radius: 20px; vertical-align: -3px; - @media screen and ( min-width: 400px ){ + + @media screen and (min-width: 400px) { height: 31px; width: 31px; } @@ -1036,46 +1052,44 @@ border-radius: 3px; padding: 0px 2px; display: inline-block; + &:hover { - @include themeable( - background, - theme-container-background-hover, - transparent - ); + @include themeable(background, + theme-container-background-hover, + transparent); } } -.chatmessagebody__divider { - -} +.chatmessagebody__divider {} .chatmessagebody__message { p { margin-top: 0; margin-bottom: 9px; + &:last-child { margin-bottom: 0; } } + blockquote { border-left: 4px solid $black; margin-left: 0px; padding-left: 8px; } + a h1 { - @include themeable( - color, - theme-container-color, - $black - ); - @include themeable-important( - border-color, - theme-container-color, - #0a0a0a - ); + @include themeable(color, + theme-container-color, + $black); + @include themeable-important(border-color, + theme-container-color, + #0a0a0a); } - img { max-width: 100%; } + img { + max-width: 100%; + } } .chatmessagebody__currentuser { @@ -1099,21 +1113,15 @@ padding: 5px; resize: none; flex-grow: 1; - @include themeable( - background, - theme-container-background, - white - ); - @include themeable( - border, - theme-border, - 1px solid $outline-color - ); - @include themeable( - color, - theme-color, - $black - ); + @include themeable(background, + theme-container-background, + white); + @include themeable(border, + theme-border, + 1px solid $outline-color); + @include themeable(color, + theme-color, + $black); } .messagecomposer__submit { @@ -1124,10 +1132,12 @@ font-family: $helvetica-condensed; color: white; font-size: 18px; - @media screen and ( min-width: 500px ){ + + @media screen and (min-width: 500px) { width: 80px; } - @media screen and ( min-width: 1500px ){ + + @media screen and (min-width: 1500px) { width: 100px; } } @@ -1136,31 +1146,30 @@ height: 5px; } -.chatcodeeditor{ +.chatcodeeditor { position: absolute; top: 40px; left: 0px; right: 24px; bottom: 0px; } -.chatcodeeditor__header{ + +.chatcodeeditor__header { position: absolute; top: -27px; left: 35px; font-family: $monospace; - @include themeable( - color, - theme-color, - $dark-gray - ); + @include themeable(color, + theme-color, + $dark-gray); } -.CodeMirror{ +.CodeMirror { padding: 12px; - font-size:0.9em; + font-size: 0.9em; } -.cursorelement{ +.cursorelement { border-left: 3px solid $red; animation: blinker 1s linear infinite; padding: 0px; @@ -1192,24 +1201,27 @@ box-shadow: 0px 0px 0px #ffffff !important; border: 0px !important; margin-top: 8px !important; - .title{ - width:95%; - font-size:18px; - h1{ - font-size:28px !important; + + .title { + width: 95%; + font-size: 18px; + + h1 { + font-size: 28px !important; } } + .body { - font-size:20px; - width:96%; + font-size: 20px; + width: 96%; } } -.chatchanneljumpback{ +.chatchanneljumpback { position: relative; } -.chatchanneljumpback__messages{ +.chatchanneljumpback__messages { position: absolute; bottom: 12px; right: 8px; @@ -1224,29 +1236,35 @@ font-weight: bold; } -.chatchanneljumpback__hide{ +.chatchanneljumpback__hide { display: none; } -.message__info__actions{ +.message__info__actions { display: grid; grid-template-columns: 4fr 1fr; } -.message__actions{ + +.message__actions { display: none; justify-content: end; + -webkit-justify-content: flex-end; + } + .message__actions span { - margin-left: 7px; - font-size: 13px; - font-weight: bold; - cursor: pointer; + margin-left: 7px; + font-size: 13px; + font-weight: bold; + cursor: pointer; } -.chatmessage:hover .message__actions{ - display: flex; + +.chatmessage:hover .message__actions { + display: -webkit-flex; + display: flex; } -.message__delete__modal{ +.message__delete__modal { position: fixed; background-color: rgba(255, 255, 255, 0.25); top: 0; @@ -1257,38 +1275,43 @@ display: block; transition: all 0.3s; } -.modal__content{ + +.modal__content { width: 400px; position: absolute; top: 50%; left: 50%; -webkit-transform: translate(-50%, -50%); - transform: translate(-50%, -50%); + transform: translate(-50%, -50%); padding: 1em; border: 2px solid; background: var(--theme-background, #f5f6f7); color: var(--theme-color, $black); } -.modal__content h3{ + +.modal__content h3 { margin: 20px 0; text-align: center; } -.delete__action__buttons{ - display: flex; + +.delete__action__buttons { + display: flex !important; justify-content: space-around; margin: 40px 0px 20px 0; } .delete__action__buttons div { - border: 2px solid var(--theme-color, $black); - padding: 5px 20px; - cursor: pointer; - user-select: none; + border: 2px solid var(--theme-color, $black); + padding: 5px 20px; + cursor: pointer; + user-select: none; } -.message__delete__modal__hide{ + +.message__delete__modal__hide { display: none; } -.message__delete__button{ + +.message__delete__button { background: #ff0000; color: white; -} \ No newline at end of file +} diff --git a/app/controllers/messages_controller.rb b/app/controllers/messages_controller.rb index 7a91fe9f47d5e..6556b96a4f077 100644 --- a/app/controllers/messages_controller.rb +++ b/app/controllers/messages_controller.rb @@ -8,16 +8,25 @@ def create @temp_message_id = (0...20).map { ("a".."z").to_a[rand(8)] }.join authorize @message + # sending temp message only to sender if @message.valid? begin message_json = create_pusher_payload(@message, @temp_message_id) - Pusher.trigger(@message.chat_channel.pusher_channels, "message-created", message_json) + Pusher.trigger("private-message-notifications-#{@message.user_id}", "message-created", message_json) rescue Pusher::Error => e logger.info "PUSHER ERROR: #{e.message}" end end if @message.save + if @message.valid? + begin + message_json = create_pusher_payload(@message, @temp_message_id) + Pusher.trigger(@message.chat_channel.pusher_channels, "message-created", message_json) + rescue Pusher::Error => e + logger.info "PUSHER ERROR: #{e.message}" + end + end render json: { status: "success", message: { temp_id: @temp_message_id, id: @message.id } }, status: :created else render json: { @@ -61,6 +70,7 @@ def destroy def create_pusher_payload(new_message, temp_id) { temp_id: temp_id, + id: new_message.id, user_id: new_message.user.id, chat_channel_id: new_message.chat_channel.id, chat_channel_adjusted_slug: new_message.chat_channel.adjusted_slug(current_user, "sender"), @@ -78,7 +88,6 @@ def message_params end def set_message - logger.info "PUSHER ERROR: #{params[:id]}" @message = Message.find(params[:id]) end diff --git a/app/javascript/chat/chat.jsx b/app/javascript/chat/chat.jsx index 6f70d1f0230f6..ac0ea020b722d 100644 --- a/app/javascript/chat/chat.jsx +++ b/app/javascript/chat/chat.jsx @@ -310,11 +310,11 @@ export default class Chat extends Component { const { activeChannelId } = this.state; this.setState(prevState => ({ messages: { - [activeChannelId]: { + [activeChannelId]: [ ...prevState.messages[activeChannelId].filter( oldmessage => oldmessage.id !== message.id, ), - }, + ], }, })); }; @@ -324,6 +324,15 @@ export default class Chat extends Component { const receivedChatChannelId = message.chat_channel_id; let newMessages = []; + if ( + message.temp_id && + messages[activeChannelId].findIndex( + oldmessage => oldmessage.temp_id === message.temp_id, + ) > -1 + ) { + return; + } + if (messages[receivedChatChannelId]) { newMessages = messages[receivedChatChannelId].slice(); newMessages.push(message); From 8d402cfa9986b5389e8779e1f38de4f2e2022455 Mon Sep 17 00:00:00 2001 From: Sarthak <7lovesharma7@gmail.com> Date: Mon, 9 Dec 2019 21:49:01 +0530 Subject: [PATCH 05/10] =?UTF-8?q?Refactoring=F0=9F=9B=A0:=20Message=20cont?= =?UTF-8?q?roller=20refactoring?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/controllers/messages_controller.rb | 34 +++++++++++++------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/app/controllers/messages_controller.rb b/app/controllers/messages_controller.rb index 6556b96a4f077..5771fe511418a 100644 --- a/app/controllers/messages_controller.rb +++ b/app/controllers/messages_controller.rb @@ -9,24 +9,9 @@ def create authorize @message # sending temp message only to sender - if @message.valid? - begin - message_json = create_pusher_payload(@message, @temp_message_id) - Pusher.trigger("private-message-notifications-#{@message.user_id}", "message-created", message_json) - rescue Pusher::Error => e - logger.info "PUSHER ERROR: #{e.message}" - end - end - + pusher_message_created(true) if @message.save - if @message.valid? - begin - message_json = create_pusher_payload(@message, @temp_message_id) - Pusher.trigger(@message.chat_channel.pusher_channels, "message-created", message_json) - rescue Pusher::Error => e - logger.info "PUSHER ERROR: #{e.message}" - end - end + pusher_message_created(false) render json: { status: "success", message: { temp_id: @temp_message_id, id: @message.id } }, status: :created else render json: { @@ -105,4 +90,19 @@ def user_not_authorized end end end + + def pusher_message_created(is_single) + return unless @message.valid? + + begin + message_json = create_pusher_payload(@message, @temp_message_id) + if is_single + Pusher.trigger("private-message-notifications-#{@message.user_id}", "message-created", message_json) + else + Pusher.trigger(@message.chat_channel.pusher_channels, "message-created", message_json) + end + rescue Pusher::Error => e + logger.info "PUSHER ERROR: #{e.message}" + end + end end From a8bd27c299c5372ba837bb2bf2b7384325872bf5 Mon Sep 17 00:00:00 2001 From: Sarthak <7lovesharma7@gmail.com> Date: Tue, 10 Dec 2019 16:41:59 +0530 Subject: [PATCH 06/10] =?UTF-8?q?Test=20Cases=F0=9F=93=9D=20:=20Specs=20fo?= =?UTF-8?q?r=20Delete=20message=20added?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- spec/requests/messages_spec.rb | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/spec/requests/messages_spec.rb b/spec/requests/messages_spec.rb index 19a22bfc2bd04..fab47c959af09 100644 --- a/spec/requests/messages_spec.rb +++ b/spec/requests/messages_spec.rb @@ -9,6 +9,7 @@ { message_markdown: "hi", user_id: user.id, + temp_id: "sd78jdssd", chat_channel_id: chat_channel.id } end @@ -26,6 +27,7 @@ end it "returns 201 upon success" do + allow(Pusher).to receive(:trigger).and_return(true) expect(response.status).to eq(201) end @@ -46,4 +48,28 @@ end end end + + describe "DELETE /messages/:id" do + let(:old_message) { create(:message, user_id: user.id) } + + it "requires user to be signed in" do + expect { delete "/messages/#{old_message.id}" }.to raise_error(Pundit::NotAuthorizedError) + end + + context "when user is signed in" do + before do + allow(Pusher).to receive(:trigger).and_return(true) + sign_in user + delete "/messages/#{old_message.id}", params: { message: old_message } + end + + it "returns message deleted" do + expect(response.body).to include "deleted" + end + + it "returns in json" do + expect(response.content_type).to eq("application/json") + end + end + end end From bb805eda39613c1815c554d75e94bbe868f6351d Mon Sep 17 00:00:00 2001 From: Sarthak <7lovesharma7@gmail.com> Date: Mon, 16 Dec 2019 22:34:20 +0530 Subject: [PATCH 07/10] =?UTF-8?q?Feature=20=F0=9F=9A=80=20:=20Ability=20to?= =?UTF-8?q?=20edit=20messages?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .vscode/settings.json | 18 ++- app/assets/stylesheets/chat.scss | 31 +++++ app/controllers/messages_controller.rb | 29 ++++- .../__snapshots__/chat.test.jsx.snap | 34 +++--- .../__snapshots__/compose.test.jsx.snap | 34 +++--- .../__snapshots__/message.test.jsx.snap | 8 +- app/javascript/chat/actions.js | 28 +++++ app/javascript/chat/chat.jsx | 80 +++++++++++++ app/javascript/chat/compose.jsx | 106 ++++++++++++++---- app/javascript/chat/message.jsx | 27 ++++- .../src/utils/getUnopenedChannels.jsx | 3 + app/javascript/src/utils/pusher.js | 1 + app/policies/message_policy.rb | 8 ++ app/views/chat_channels/show.json.jbuilder | 2 + config/routes.rb | 1 + ...0191215145706_add_edited_at_to_messages.rb | 5 + db/schema.rb | 3 +- 17 files changed, 359 insertions(+), 59 deletions(-) create mode 100644 db/migrate/20191215145706_add_edited_at_to_messages.rb diff --git a/.vscode/settings.json b/.vscode/settings.json index 7de1b8db04feb..226bd287239fe 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -7,5 +7,21 @@ } }, "ruby.format": "rubocop", - "ruby.intellisense": "rubyLocate" + "ruby.intellisense": "rubyLocate", + "workbench.colorCustomizations": { + "activityBar.background": "#ab307e", + "activityBar.activeBorder": "#25320e", + "activityBar.foreground": "#e7e7e7", + "activityBar.inactiveForeground": "#e7e7e799", + "activityBarBadge.background": "#25320e", + "activityBarBadge.foreground": "#e7e7e7", + "titleBar.activeBackground": "#832561", + "titleBar.inactiveBackground": "#83256199", + "titleBar.activeForeground": "#e7e7e7", + "titleBar.inactiveForeground": "#e7e7e799", + "statusBar.background": "#832561", + "statusBarItem.hoverBackground": "#ab307e", + "statusBar.foreground": "#e7e7e7" + }, + "peacock.color": "#832561" } diff --git a/app/assets/stylesheets/chat.scss b/app/assets/stylesheets/chat.scss index f665c5492fb88..2d24038f5d4d7 100644 --- a/app/assets/stylesheets/chat.scss +++ b/app/assets/stylesheets/chat.scss @@ -1104,6 +1104,7 @@ display: flex; height: inherit; align-items: stretch; + position: relative; } .messagecomposer__input { @@ -1315,3 +1316,33 @@ background: #ff0000; color: white; } + +.messageToBeEdited { + position: absolute; + display: grid; + grid-template-columns: 1fr 40px; + width: calc(100% - 14px); + top: -53px; + background: white; + padding: 0px 5px; + border: 1px solid var(--theme-top-bar-background, #dbdbdb); + border-left: 3px solid #4e57ef; + height: 50px; + overflow: hidden; +} + +.closeEdit { + text-align: right; + user-select: none; + cursor: pointer; + padding: 2px 5px; + color: #4e57ef; + font-weight: bold; +} + +.editHead { + position: absolute; + color: #4e57ef; + font-size: 14px; + font-weight: bold; +} diff --git a/app/controllers/messages_controller.rb b/app/controllers/messages_controller.rb index 5771fe511418a..4421b7b47e2d5 100644 --- a/app/controllers/messages_controller.rb +++ b/app/controllers/messages_controller.rb @@ -1,5 +1,5 @@ class MessagesController < ApplicationController - before_action :set_message, only: %i[destroy] + before_action :set_message, only: %i[destroy update] before_action :authenticate_user!, only: %i[create] def create @@ -50,6 +50,31 @@ def destroy end end + def update + authorize @message + + if @message.update(permitted_attributes(@message).merge(edited_at: Time.zone.now)) + if @message.valid? + begin + message_json = create_pusher_payload(@message, "") + Pusher.trigger(@message.chat_channel.pusher_channels, "message-edited", message_json) + rescue Pusher::Error => e + logger.info "PUSHER ERROR: #{e.message}" + end + end + render json: { status: "success", message: "Message was edited" } + else + render json: { + status: "error", + message: { + chat_channel_id: @message.chat_channel_id, + message: @message.errors.full_messages, + type: "error" + } + }, status: :unauthorized + end + end + private def create_pusher_payload(new_message, temp_id) @@ -62,6 +87,8 @@ def create_pusher_payload(new_message, temp_id) username: new_message.user.username, profile_image_url: ProfileImage.new(new_message.user).get(90), message: new_message.message_html, + markdown: new_message.message_markdown, + edited_at: new_message.edited_at, timestamp: Time.current, color: new_message.preferred_user_color, reception_method: "pushed" diff --git a/app/javascript/chat/__tests__/__snapshots__/chat.test.jsx.snap b/app/javascript/chat/__tests__/__snapshots__/chat.test.jsx.snap index 22ee18eec3f04..3ee97f2fac9bc 100644 --- a/app/javascript/chat/__tests__/__snapshots__/chat.test.jsx.snap +++ b/app/javascript/chat/__tests__/__snapshots__/chat.test.jsx.snap @@ -244,23 +244,25 @@ exports[` should load chat 1`] = `
-
-