diff --git a/app/assets/stylesheets/chat.scss b/app/assets/stylesheets/chat.scss
index 2d24038f5..ca2c554b1 100644
--- a/app/assets/stylesheets/chat.scss
+++ b/app/assets/stylesheets/chat.scss
@@ -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;
diff --git a/app/assets/stylesheets/scaffolds.scss b/app/assets/stylesheets/scaffolds.scss
index bfc3873bc..f8cf553a2 100644
--- a/app/assets/stylesheets/scaffolds.scss
+++ b/app/assets/stylesheets/scaffolds.scss
@@ -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
diff --git a/app/controllers/chat_channels_controller.rb b/app/controllers/chat_channels_controller.rb
index 86a95c9fe..c92a3a1af 100644
--- a/app/controllers/chat_channels_controller.rb
+++ b/app/controllers/chat_channels_controller.rb
@@ -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
diff --git a/app/javascript/chat/__tests__/__snapshots__/message.test.jsx.snap b/app/javascript/chat/__tests__/__snapshots__/message.test.jsx.snap
index db8d9b527..772bf7766 100644
--- a/app/javascript/chat/__tests__/__snapshots__/message.test.jsx.snap
+++ b/app/javascript/chat/__tests__/__snapshots__/message.test.jsx.snap
@@ -33,7 +33,7 @@ exports[` should render and test snapshot 1`] = `
class="message__info"
>
(
incomingVideoCallChannelIds={[]} // no incoming calls
activeChannelId={12345}
chatChannels={chatChannels}
+ unopenedChannelIds={[]}
handleSwitchChannel={fakeSwitchChannel}
channelsLoaded
filterQuery=""
diff --git a/app/javascript/chat/actions.js b/app/javascript/chat/actions.js
index f492a456d..780b18158 100644
--- a/app/javascript/chat/actions.js
+++ b/app/javascript/chat/actions.js
@@ -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',
diff --git a/app/javascript/chat/channels.jsx b/app/javascript/chat/channels.jsx
index c9b88098f..17abc35e4 100644
--- a/app/javascript/chat/channels.jsx
+++ b/app/javascript/chat/channels.jsx
@@ -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,
diff --git a/app/javascript/chat/chat.jsx b/app/javascript/chat/chat.jsx
index 0e2bd1b2f..30293194e 100644
--- a/app/javascript/chat/chat.jsx
+++ b/app/javascript/chat/chat.jsx
@@ -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 => (
@@ -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) {
diff --git a/app/javascript/chat/message.jsx b/app/javascript/chat/message.jsx
index 0813ad722..02a5d8afe 100644
--- a/app/javascript/chat/message.jsx
+++ b/app/javascript/chat/message.jsx
@@ -59,7 +59,7 @@ const Message = ({
>