Add chat channel indicator (#379)

* Add new message indicator to chat

* Add indicator switch to chat

* Add chat switch indicator
This commit is contained in:
Ben Halpern 2018-06-01 18:16:10 -04:00 committed by GitHub
parent 4171a0814a
commit 10c3b52b0a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 96 additions and 8 deletions

View file

@ -121,6 +121,21 @@
box-shadow: 3px 3px 0px $dark-purple;
}
.chatchanneltabindicator{
display: inline-block;
background: white;
border: 2px solid $light-medium-gray;
padding: 5px;
border-radius: 100px;
vertical-align: -2px;
margin-right: 3px;
}
.chatchanneltabindicator--new{
background: $yellow;
border: 2px solid $black;
}
@keyframes example {
// animation-name: example;
// animation-duration: 4s;
@ -157,11 +172,11 @@
}
.chatmessagebody__profileimage {
height: 18px;
width: 18px;
margin-right: 3px;
height: 23px;
width: 23px;
margin-right: 6px;
border-radius: 20px;
vertical-align: bottom;
vertical-align: -5px;
}
.chatmessagebody__username {

View file

@ -12,6 +12,13 @@ class ChatChannelsController < ApplicationController
end
end
def open
@chat_channel = ChatChannel.find(params[:id])
raise unless @chat_channel.has_member?(current_user)
@chat_channel.chat_channel_memberships.where(user_id: current_user.id).first.touch(:last_opened_at)
render json: { status: "success", channel: params[:id] }, status: 200
end
def moderate
@chat_channel = ChatChannel.find(params[:id])
authorize @chat_channel

View file

@ -43,7 +43,12 @@ class PagesController < ApplicationController
end
def chat
@chat_channels = current_user.chat_channels.order("last_message_at DESC")
@chat_channels = current_user.chat_channels.
order("last_message_at DESC").
includes(:chat_channel_memberships)
@chat_channels.each do |channel|
channel.current_user = current_user
end
slug = if params[:slug] && params[:slug].start_with?("@")
[current_user.username, params[:slug].gsub("@", "")].sort.join("/")
else

View file

@ -30,6 +30,22 @@ export function sendMessage(activeChannelId, message, successCb, failureCb) {
.catch(failureCb);
}
export function sendOpen(activeChannelId, successCb, failureCb) {
fetch(`/chat_channels/${activeChannelId}/open`, {
method: 'POST',
headers: {
Accept: 'application/json',
'X-CSRF-Token': window.csrfToken,
'Content-Type': 'application/json',
},
body: JSON.stringify({}),
credentials: 'same-origin',
})
.then(response => response.json())
.then(successCb)
.catch(failureCb);
}
export function conductModeration(
activeChannelId,
message,

View file

@ -8,6 +8,7 @@ const Channels = ({ activeChannelId, chatChannels, handleSwitchChannel }) => {
? 'chatchanneltab--active'
: 'chatchanneltab--inactive';
const name = channel.channel_type === "direct" ? '@'+channel.slug.replace(`${window.currentUser.username}/`, '').replace(`/${window.currentUser.username}`, '') : channel.channel_name
const newMessagesIndicatorClass = new Date(channel.last_opened_at) < new Date(channel.last_message_at) ? "chatchanneltabindicator--new" : "chatchanneltabindicator--old"
return (
<button
className='chatchanneltabbutton'
@ -20,7 +21,7 @@ const Channels = ({ activeChannelId, chatChannels, handleSwitchChannel }) => {
data-channel-id={channel.id}
data-channel-name={name}
>
{name}
<span className={"chatchanneltabindicator " + newMessagesIndicatorClass}></span> {name}
</span>
</button>
);

View file

@ -1,6 +1,6 @@
import { h, Component } from 'preact';
import PropTypes from 'prop-types';
import { conductModeration, getAllMessages, sendMessage } from './actions';
import { conductModeration, getAllMessages, sendMessage, sendOpen } from './actions';
import { hideMessages, scrollToBottom, setupObserver } from './util';
import Alert from './alert';
import Channels from './channels';
@ -54,6 +54,11 @@ export default class Chat extends Component {
channelCleared: this.clearChannel,
redactUserMessages: this.redactUserMessages,
});
sendOpen(
this.state.activeChannelId,
this.handleChannelOpenSuccess,
null,
);
};
observerCallback = entries => {
@ -83,8 +88,22 @@ export default class Chat extends Component {
this.state.activeChannelId === receivedChatChannelId
? { showAlert: this.state.scrolled }
: {};
const newChannelsObj = this.state.chatChannels.map(channel => {
if (receivedChatChannelId === channel["id"]){
channel["last_message_at"] = new Date();
}
return channel;
});
if (receivedChatChannelId === this.state.activeChannelId) {
sendOpen(
receivedChatChannelId,
this.handleChannelOpenSuccess,
null,
);
}
this.setState({
...newShowAlert,
chatChannels: newChannelsObj,
messages: {
...this.state.messages,
[receivedChatChannelId]: newMessages,
@ -148,6 +167,11 @@ export default class Chat extends Component {
showAlert: false,
});
window.history.replaceState(null, null, "/chat/"+e.target.dataset.channelName);
sendOpen(
e.target.dataset.channelId,
this.handleChannelOpenSuccess,
null,
);
};
handleSubmitOnClick = e => {
@ -165,6 +189,16 @@ export default class Chat extends Component {
}
};
handleChannelOpenSuccess = response => {
const newChannelsObj = this.state.chatChannels.map(channel => {
if (parseInt(response.channel) === channel["id"]){
channel["last_opened_at"] = new Date();
}
return channel;
});
this.setState({ chatChannels: newChannelsObj });
};
handleFailure = err => {
console.error(err);
};
@ -217,6 +251,8 @@ export default class Chat extends Component {
</div>
);
render() {
return (
<div className="chat">

View file

@ -1,4 +1,6 @@
class ChatChannel < ApplicationRecord
attr_accessor :current_user
has_many :messages
has_many :chat_channel_memberships
has_many :users, through: :chat_channel_memberships
@ -18,6 +20,11 @@ class ChatChannel < ApplicationRecord
users.include?(user)
end
def last_opened_at(user = nil)
user ||= current_user
chat_channel_memberships.where(user_id: user.id).pluck(:last_opened_at).first
end
def self.create_with_users(users, channel_type = "direct", contrived_name = "New Channel")
raise "Invalid direct channel" if users.size != 2 && channel_type == "direct"
if channel_type == "direct"

View file

@ -24,7 +24,7 @@
id="chat"
class="live-chat"
data-pusher-key="<%= ENV["PUSHER_KEY"] %>"
data-chat-channels="<%= @chat_channels.to_json %>"
data-chat-channels="<%= @chat_channels.to_json(methods: :last_opened_at) %>"
data-chat-options="<%= {showChannelsList:true, showTimestamp: true, activeChannelId: @active_channel.id }.to_json %>">
</div>
<% else %>

View file

@ -98,6 +98,7 @@ Rails.application.routes.draw do
patch "/onboarding_update" => "users#onboarding_update"
get "email_subscriptions/unsubscribe"
post "chat_channels/:id/moderate" => "chat_channels#moderate"
post "chat_channels/:id/open" => "chat_channels#open"
# resources :users
get "/social_previews/article/:id" => "social_previews#article"