Separate chat channel unopened status from Algolia (#5198) [deploy]
This commit is contained in:
parent
b1a71d0ac1
commit
cb3a0c462c
10 changed files with 85 additions and 15 deletions
|
|
@ -798,11 +798,12 @@
|
|||
}
|
||||
|
||||
.chatchanneltabbutton {
|
||||
width: 93%;
|
||||
width: calc(95% - 2px);
|
||||
border: none;
|
||||
background: transparent;
|
||||
padding: 3px 0px;
|
||||
margin-bottom: -5px 0;
|
||||
box-sizing: border-box;
|
||||
@include themeable(color,
|
||||
theme-color,
|
||||
$black);
|
||||
|
|
@ -817,7 +818,7 @@
|
|||
|
||||
.chatchanneltab {
|
||||
display: inline-block;
|
||||
width: 90%;
|
||||
width: 100%;
|
||||
margin-bottom: 5px;
|
||||
background: inherit;
|
||||
text-align: start;
|
||||
|
|
@ -829,6 +830,7 @@
|
|||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
box-sizing: border-box;
|
||||
|
||||
@media screen and (min-width: 550px) {
|
||||
font-size: 13px;
|
||||
|
|
|
|||
|
|
@ -207,6 +207,10 @@ body.night-theme, body.ten-x-hacker-theme {
|
|||
.partner-image-light-mode {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.not-dark-theme-text-compatible {
|
||||
@include themeable-important(color, theme-secondary-color, white);
|
||||
}
|
||||
}
|
||||
|
||||
// Alternate base fonts
|
||||
|
|
|
|||
|
|
@ -7,6 +7,9 @@ class ChatChannelsController < ApplicationController
|
|||
if params[:state] == "unopened"
|
||||
authorize ChatChannel
|
||||
render_unopened_json_response
|
||||
elsif params[:state] == "unopened_ids"
|
||||
authorize ChatChannel
|
||||
render_unopened_ids_response
|
||||
elsif params[:state] == "pending"
|
||||
authorize ChatChannel
|
||||
render_pending_json_response
|
||||
|
|
@ -134,6 +137,12 @@ class ChatChannelsController < ApplicationController
|
|||
render "index.json"
|
||||
end
|
||||
|
||||
def render_unopened_ids_response
|
||||
@unopened_ids = ChatChannelMembership.where(user_id: session_current_user_id).includes(:chat_channel).
|
||||
where(has_unopened_messages: true).pluck(:chat_channel_id)
|
||||
render json: { unopened_ids: @unopened_ids }
|
||||
end
|
||||
|
||||
def render_channels_html
|
||||
return unless current_user
|
||||
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ exports[`<Message /> should render and test snapshot 1`] = `
|
|||
class="message__info"
|
||||
>
|
||||
<span
|
||||
class="chatmessagebody__username"
|
||||
class="chatmessagebody__username not-dark-theme-text-compatible"
|
||||
style={
|
||||
Object {
|
||||
"color": "#00FFFF",
|
||||
|
|
|
|||
|
|
@ -78,6 +78,7 @@ const getChannels = (mod, chatChannels) => (
|
|||
incomingVideoCallChannelIds={[]} // no incoming calls
|
||||
activeChannelId={12345}
|
||||
chatChannels={chatChannels}
|
||||
unopenedChannelIds={[]}
|
||||
handleSwitchChannel={fakeSwitchChannel}
|
||||
channelsLoaded
|
||||
filterQuery=""
|
||||
|
|
|
|||
|
|
@ -137,6 +137,16 @@ export function getChannels(
|
|||
});
|
||||
}
|
||||
|
||||
export function getUnopenedChannelIds(successCb) {
|
||||
fetch('/chat_channels?state=unopened_ids', {
|
||||
credentials: 'same-origin',
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(json => {
|
||||
successCb(json.unopened_ids);
|
||||
});
|
||||
}
|
||||
|
||||
export function getTwilioToken(videoChannelName, successCb, failureCb) {
|
||||
fetch(`/twilio_tokens/${videoChannelName}`, {
|
||||
Accept: 'application/json',
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import ConfigImage from 'images/three-dots.svg';
|
|||
const Channels = ({
|
||||
activeChannelId,
|
||||
chatChannels,
|
||||
unopenedChannelIds,
|
||||
handleSwitchChannel,
|
||||
expanded,
|
||||
filterQuery,
|
||||
|
|
@ -14,10 +15,7 @@ const Channels = ({
|
|||
}) => {
|
||||
const channels = chatChannels.map(channel => {
|
||||
const isActive = parseInt(activeChannelId, 10) === channel.chat_channel_id;
|
||||
const lastOpened = channel.last_opened_at;
|
||||
const isUnopened =
|
||||
new Date(channel.channel_last_message_at) > new Date(lastOpened) &&
|
||||
channel.channel_messages_count > 0;
|
||||
const isUnopened = !isActive && unopenedChannelIds.includes(channel.chat_channel_id)
|
||||
let newMessagesIndicator = isUnopened ? 'new' : 'old';
|
||||
if (incomingVideoCallChannelIds.indexOf(channel.chat_channel_id) > -1) {
|
||||
newMessagesIndicator = 'video';
|
||||
|
|
@ -114,6 +112,7 @@ const Channels = ({
|
|||
Channels.propTypes = {
|
||||
activeChannelId: PropTypes.number.isRequired,
|
||||
chatChannels: PropTypes.arrayOf(PropTypes.objectOf()).isRequired,
|
||||
unopenedChannelIds: PropTypes.arrayOf().isRequired,
|
||||
handleSwitchChannel: PropTypes.func.isRequired,
|
||||
expanded: PropTypes.bool.isRequired,
|
||||
filterQuery: PropTypes.string.isRequired,
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import {
|
|||
sendMessage,
|
||||
sendOpen,
|
||||
getChannels,
|
||||
getUnopenedChannelIds,
|
||||
getContent,
|
||||
getChannelInvites,
|
||||
sendChannelInviteAction,
|
||||
|
|
@ -41,6 +42,7 @@ export default class Chat extends Component {
|
|||
scrolled: false,
|
||||
showAlert: false,
|
||||
chatChannels,
|
||||
unopenedChannelIds: [],
|
||||
filterQuery: '',
|
||||
channelTypeFilter: 'all',
|
||||
channelsLoaded: false,
|
||||
|
|
@ -92,9 +94,6 @@ export default class Chat extends Component {
|
|||
channel => `open-channel-${channel.chat_channel_id}`,
|
||||
);
|
||||
setupObserver(this.observerCallback);
|
||||
if (!window.currentUser) {
|
||||
window.currentUser = JSON.parse(document.body.dataset.user);
|
||||
}
|
||||
this.subscribePusher(
|
||||
`private-message-notifications-${currentUserId}`,
|
||||
);
|
||||
|
|
@ -114,6 +113,7 @@ export default class Chat extends Component {
|
|||
filters,
|
||||
this.loadChannels,
|
||||
);
|
||||
getUnopenedChannelIds(this.markUnopenedChannelIds)
|
||||
}
|
||||
if (!isMobileDevice) {
|
||||
document.getElementById('messageform').focus();
|
||||
|
|
@ -237,6 +237,10 @@ export default class Chat extends Component {
|
|||
document.getElementById('chatchannels__channelslist').scrollTop = 0;
|
||||
};
|
||||
|
||||
markUnopenedChannelIds = (ids) => {
|
||||
this.setState({unopenedChannelIds: ids})
|
||||
}
|
||||
|
||||
subscribeChannelsToPusher = (channels, channelNameFn) => {
|
||||
channels.forEach(channel => {
|
||||
this.subscribePusher(channelNameFn(channel));
|
||||
|
|
@ -341,7 +345,7 @@ export default class Chat extends Component {
|
|||
};
|
||||
|
||||
receiveNewMessage = message => {
|
||||
const { messages, activeChannelId, scrolled, chatChannels } = this.state;
|
||||
const { messages, activeChannelId, scrolled, chatChannels, unopenedChannelIds } = this.state;
|
||||
const receivedChatChannelId = message.chat_channel_id;
|
||||
let newMessages = [];
|
||||
if (
|
||||
|
|
@ -389,7 +393,16 @@ export default class Chat extends Component {
|
|||
|
||||
if (receivedChatChannelId === activeChannelId) {
|
||||
sendOpen(receivedChatChannelId, this.handleChannelOpenSuccess, null);
|
||||
} else {
|
||||
const newUnopenedChannels = unopenedChannelIds
|
||||
if (!unopenedChannelIds.includes(receivedChatChannelId)) {
|
||||
newUnopenedChannels.push(receivedChatChannelId)
|
||||
}
|
||||
this.setState({
|
||||
unopenedChannelIds: newUnopenedChannels
|
||||
})
|
||||
}
|
||||
|
||||
this.setState(prevState => ({
|
||||
...newShowAlert,
|
||||
chatChannels: newChannelsObj,
|
||||
|
|
@ -545,7 +558,7 @@ export default class Chat extends Component {
|
|||
target = target.parentElement;
|
||||
}
|
||||
this.triggerSwitchChannel(
|
||||
target.dataset.channelId,
|
||||
parseInt(target.dataset.channelId, 10),
|
||||
target.dataset.channelSlug,
|
||||
);
|
||||
};
|
||||
|
|
@ -581,12 +594,18 @@ export default class Chat extends Component {
|
|||
};
|
||||
|
||||
triggerSwitchChannel = (id, slug) => {
|
||||
const { chatChannels, isMobileDevice } = this.state;
|
||||
const { chatChannels, isMobileDevice, unopenedChannelIds } = this.state;
|
||||
const newUnopenedChannelIds = unopenedChannelIds
|
||||
const index = newUnopenedChannelIds.indexOf(id);
|
||||
if (index > -1) {
|
||||
newUnopenedChannelIds.splice(index, 1);
|
||||
}
|
||||
this.setState({
|
||||
activeChannel: this.filterForActiveChannel(chatChannels, id),
|
||||
activeChannelId: parseInt(id, 10),
|
||||
scrolled: false,
|
||||
showAlert: false,
|
||||
unopenedChannelIds: unopenedChannelIds.filter(unopenedId => unopenedId !== id)
|
||||
});
|
||||
this.setupChannel(id);
|
||||
window.history.replaceState(null, null, `/connect/${slug}`);
|
||||
|
|
@ -838,7 +857,7 @@ export default class Chat extends Component {
|
|||
}
|
||||
return messages[activeChannelId].map(message => (
|
||||
<Message
|
||||
currentUserId={window.currentUser.id}
|
||||
currentUserId={currentUserId}
|
||||
id={message.id}
|
||||
user={message.username}
|
||||
userID={message.user_id}
|
||||
|
|
@ -954,6 +973,7 @@ export default class Chat extends Component {
|
|||
<Channels
|
||||
activeChannelId={state.activeChannelId}
|
||||
chatChannels={state.chatChannels}
|
||||
unopenedChannelIds={state.unopenedChannelIds}
|
||||
handleSwitchChannel={this.handleSwitchChannel}
|
||||
channelsLoaded={state.channelsLoaded}
|
||||
filterQuery={state.filterQuery}
|
||||
|
|
@ -979,6 +999,7 @@ export default class Chat extends Component {
|
|||
incomingVideoCallChannelIds={state.incomingVideoCallChannelIds}
|
||||
activeChannelId={state.activeChannelId}
|
||||
chatChannels={state.chatChannels}
|
||||
unopenedChannelIds={state.unopenedChannelIds}
|
||||
handleSwitchChannel={this.handleSwitchChannel}
|
||||
expanded={state.expanded}
|
||||
/>
|
||||
|
|
@ -997,6 +1018,10 @@ export default class Chat extends Component {
|
|||
messageOffset,
|
||||
} = this.state;
|
||||
|
||||
if (!messages[activeChannelId]) {
|
||||
return;
|
||||
}
|
||||
|
||||
const jumpbackButton = document.getElementById('jumpback_button');
|
||||
|
||||
if (this.scroller) {
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ const Message = ({
|
|||
>
|
||||
<div className="message__info__actions">
|
||||
<div className="message__info">
|
||||
<span className="chatmessagebody__username" style={spanStyle}>
|
||||
<span className="chatmessagebody__username not-dark-theme-text-compatible" style={spanStyle}>
|
||||
<a
|
||||
className="chatmessagebody__username--link"
|
||||
href={`/${user}`}
|
||||
|
|
|
|||
|
|
@ -50,6 +50,26 @@ RSpec.describe "ChatChannels", type: :request do
|
|||
end
|
||||
end
|
||||
|
||||
describe "get /chat_channels?state=unopened_ids" do
|
||||
it "returns unopened chat channel ids" do
|
||||
direct_channel.add_users [user]
|
||||
user.chat_channel_memberships.each { |m| m.update(has_unopened_messages: true) }
|
||||
sign_in user
|
||||
get "/chat_channels?state=unopened_ids"
|
||||
expect(response.body).to include(direct_channel.id.to_s)
|
||||
expect(response.body).to include("unopened_ids")
|
||||
end
|
||||
|
||||
it "does not return chat channel ids if not signed in" do
|
||||
direct_channel.add_users [user]
|
||||
user.chat_channel_memberships.each { |m| m.update(has_unopened_messages: true) }
|
||||
sign_out user
|
||||
expect do
|
||||
get "/chat_channels?state=unopened_ids"
|
||||
end.to raise_error(Pundit::NotAuthorizedError)
|
||||
end
|
||||
end
|
||||
|
||||
describe "get /chat_channels?state=pending" do
|
||||
it "returns pending channels" do
|
||||
ChatChannelMembership.create(chat_channel_id: invite_channel.id, user_id: user.id, status: "pending")
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue