* Feature 🚀 : Ability to delete messages in chat channels - Sending message ID to frontend - Deleting Message - Use pusher to delete message realtime * Minor Bug 🐞: Show message action only for current user - User can delete or edit their own messages * Test cases added * Bug 🐞: Update message id for receiver Message id was not sent to receiver by pusher * Refactoring🛠: Message controller refactoring * Test Cases📝 : Specs for Delete message added * Feature 🚀 : Ability to edit messages * Test Cases📝 : Specs for Edit message added * Merge conflict resolved * UI completed for Create Channel * API for create channel & invitations * 🚀 Combining Backend and Frontend * Test cases added * UI completed for Create Channel * API for create channel & invitations * 🚀 Combining Backend and Frontend * Test cases added * fix suggestions * add test cases * replace mod_relations_admin to tag_moderator * fix test cases * refactor and fix code climate issues * Update app/controllers/chat_channel_memberships_controller.rb Co-authored-by: Michael Kohl <me@citizen428.net> * fix suggestions * renaming the methods * Update app/javascript/chat/chat.jsx Co-authored-by: Nick Taylor <nick@iamdeveloper.com> * Update app/javascript/chat/components/createChatModal.jsx Co-authored-by: Nick Taylor <nick@iamdeveloper.com> * add jsDoc and add validation * add PR suggestions * renmae the file name to PascalCase * add styled component * refactor channel button component * fix specs * fix code smell issues * fix file name issue * handle error message * add jsDoc * Update app/controllers/chat_channels_controller.rb Co-authored-by: Michael Kohl <me@citizen428.net> * refactor chat_channel_helpers * update old hash to new hash * Update app/javascript/chat/components/ChannelButton.jsx Co-authored-by: Nick Taylor <nick@iamdeveloper.com> * Update app/views/chat_channels/index.html.erb Co-authored-by: Michael Kohl <me@citizen428.net> * fix syntax error * Update app/views/chat_channels/index.html.erb Co-authored-by: Nick Taylor <nick@iamdeveloper.com> * Policies changed * The Disable Button issue 🐞 * Fixing Disabled button Issue 🐞 * New group doesn't show up 🐞 Co-authored-by: Narender Singh <narender2031@gmail.com> Co-authored-by: Michael Kohl <me@citizen428.net> Co-authored-by: Nick Taylor <nick@iamdeveloper.com>
2012 lines
63 KiB
JavaScript
2012 lines
63 KiB
JavaScript
/*
|
|
eslint-disable
|
|
consistent-return, no-unused-vars, react/destructuring-assignment,
|
|
react/no-access-state-in-setstate, react/button-has-type
|
|
*/
|
|
|
|
import { h, Component } from 'preact';
|
|
import PropTypes from 'prop-types';
|
|
import { setupPusher } from '../utilities/connect';
|
|
import notifyUser from '../utilities/connect/newMessageNotify';
|
|
import debounceAction from '../utilities/debounceAction';
|
|
import { addSnackbarItem } from '../Snackbar';
|
|
import { processImageUpload } from '../article-form/actions';
|
|
import {
|
|
conductModeration,
|
|
getAllMessages,
|
|
sendMessage,
|
|
sendOpen,
|
|
getChannels,
|
|
getUnopenedChannelIds,
|
|
getContent,
|
|
deleteMessage,
|
|
editMessage,
|
|
} from './actions/actions';
|
|
import CreateChatModal from './components/CreateChatModal';
|
|
import {
|
|
sendChannelRequest,
|
|
rejectJoiningRequest,
|
|
acceptJoiningRequest,
|
|
getChannelRequestInfo,
|
|
} from './actions/requestActions';
|
|
import {
|
|
hideMessages,
|
|
scrollToBottom,
|
|
setupObserver,
|
|
getCurrentUser,
|
|
channelSorter,
|
|
} from './util';
|
|
import Alert from './alert';
|
|
import Channels from './channels';
|
|
import Compose from './compose';
|
|
import Message from './message';
|
|
import ActionMessage from './actionMessage';
|
|
import Content from './content';
|
|
import { VideoContent } from './videoContent';
|
|
import { DragAndDropZone } from '@utilities/dragAndDrop';
|
|
import { dragAndUpload } from '@utilities/dragAndUpload';
|
|
import { Button } from '@crayons';
|
|
|
|
const NARROW_WIDTH_LIMIT = 767;
|
|
const WIDE_WIDTH_LIMIT = 1600;
|
|
|
|
export default class Chat extends Component {
|
|
static propTypes = {
|
|
pusherKey: PropTypes.number.isRequired,
|
|
chatChannels: PropTypes.string.isRequired,
|
|
chatOptions: PropTypes.string.isRequired,
|
|
githubToken: PropTypes.string.isRequired,
|
|
tagModerator: PropTypes.shape({ isTagModerator: PropTypes.bool.isRequired })
|
|
.isRequired,
|
|
};
|
|
|
|
constructor(props) {
|
|
super(props);
|
|
const chatChannels = JSON.parse(props.chatChannels);
|
|
const chatOptions = JSON.parse(props.chatOptions);
|
|
|
|
this.debouncedChannelFilter = debounceAction(
|
|
this.triggerChannelFilter.bind(this),
|
|
);
|
|
|
|
this.state = {
|
|
appDomain: document.body.dataset.appDomain,
|
|
messages: [],
|
|
scrolled: false,
|
|
showAlert: false,
|
|
chatChannels,
|
|
unopenedChannelIds: [],
|
|
filterQuery: '',
|
|
channelTypeFilter: 'all',
|
|
channelsLoaded: false,
|
|
channelPaginationNum: 0,
|
|
fetchingPaginatedChannels: false,
|
|
activeChannelId: chatOptions.activeChannelId,
|
|
activeChannel: null,
|
|
showChannelsList: chatOptions.showChannelsList,
|
|
showTimestamp: chatOptions.showTimestamp,
|
|
currentUserId: chatOptions.currentUserId,
|
|
notificationsPermission: null,
|
|
activeContent: {},
|
|
fullscreenContent: null,
|
|
videoPath: null,
|
|
expanded: window.innerWidth > NARROW_WIDTH_LIMIT,
|
|
isMobileDevice: typeof window.orientation !== 'undefined',
|
|
subscribedPusherChannels: [],
|
|
messageOffset: 0,
|
|
showDeleteModal: false,
|
|
messageDeleteId: null,
|
|
allMessagesLoaded: false,
|
|
currentMessageLocation: 0,
|
|
startEditing: false,
|
|
activeEditMessage: {},
|
|
markdownEdited: false,
|
|
searchShowing: false,
|
|
channelUsers: [],
|
|
showMemberlist: false,
|
|
memberFilterQuery: null,
|
|
rerenderIfUnchangedCheck: null,
|
|
userRequestCount: 0,
|
|
openModal: false,
|
|
isTagModerator: JSON.parse(props.tagModerator).isTagModerator,
|
|
};
|
|
if (chatOptions.activeChannelId) {
|
|
getAllMessages(chatOptions.activeChannelId, 0, this.receiveAllMessages);
|
|
}
|
|
}
|
|
|
|
componentDidMount() {
|
|
const {
|
|
chatChannels,
|
|
activeChannelId,
|
|
showChannelsList,
|
|
channelTypeFilter,
|
|
isMobileDevice,
|
|
channelPaginationNum,
|
|
currentUserId,
|
|
appDomain,
|
|
messageOffset,
|
|
} = this.state;
|
|
|
|
this.setupChannels(chatChannels);
|
|
|
|
const channelsForPusherSub = chatChannels.filter(
|
|
this.channelTypeFilterFn('open'),
|
|
);
|
|
this.subscribeChannelsToPusher(
|
|
channelsForPusherSub,
|
|
(channel) => `open-channel--${appDomain}-${channel.chat_channel_id}`,
|
|
);
|
|
|
|
setupObserver(this.observerCallback);
|
|
|
|
this.subscribePusher(
|
|
`private-message-notifications--${appDomain}-${currentUserId}`,
|
|
);
|
|
|
|
if (activeChannelId) {
|
|
sendOpen(activeChannelId, this.handleChannelOpenSuccess, null);
|
|
}
|
|
if (showChannelsList) {
|
|
const filters =
|
|
channelTypeFilter === 'all'
|
|
? {}
|
|
: { filters: `channel_type:${channelTypeFilter}` };
|
|
const searchParams = {
|
|
query: '',
|
|
retrievalID: activeChannelId,
|
|
searchType: '',
|
|
paginationNumber: channelPaginationNum,
|
|
};
|
|
if (activeChannelId !== null) {
|
|
searchParams.searchType = 'discoverable';
|
|
}
|
|
getChannels(searchParams, filters, this.loadChannels);
|
|
getUnopenedChannelIds(this.markUnopenedChannelIds);
|
|
}
|
|
if (!isMobileDevice) {
|
|
document.getElementById('messageform').focus();
|
|
}
|
|
if (document.getElementById('chatchannels__channelslist')) {
|
|
document
|
|
.getElementById('chatchannels__channelslist')
|
|
.addEventListener('scroll', this.handleChannelScroll);
|
|
}
|
|
|
|
this.handleRequestCount();
|
|
}
|
|
|
|
shouldComponentUpdate(nextProps, nextState) {
|
|
if (
|
|
this.state.rerenderIfUnchangedCheck !== nextState.rerenderIfUnchangedCheck
|
|
) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
componentDidUpdate() {
|
|
const { scrolled, currentMessageLocation } = this.state;
|
|
const messageList = document.getElementById('messagelist');
|
|
if (messageList) {
|
|
if (!scrolled) {
|
|
scrollToBottom();
|
|
}
|
|
}
|
|
|
|
if (currentMessageLocation && messageList.scrollTop === 0) {
|
|
messageList.scrollTop =
|
|
messageList.scrollHeight - (currentMessageLocation + 30);
|
|
}
|
|
}
|
|
|
|
handleRequestCount = () => {
|
|
getChannelRequestInfo().then((response) => {
|
|
const { result } = response;
|
|
const { user_joining_requests, channel_joining_memberships } = result;
|
|
let totalRequest =
|
|
user_joining_requests?.length + channel_joining_memberships?.length;
|
|
this.setState({
|
|
userRequestCount: totalRequest,
|
|
});
|
|
});
|
|
};
|
|
|
|
filterForActiveChannel = (channels, id, currentUserId) =>
|
|
channels.filter(
|
|
(channel) =>
|
|
channel.chat_channel_id === parseInt(id, 10) &&
|
|
channel.viewable_by === parseInt(currentUserId, 10),
|
|
)[0];
|
|
|
|
subscribePusher = (channelName) => {
|
|
const { subscribedPusherChannels } = this.state;
|
|
const { pusherKey } = this.props;
|
|
if (!subscribedPusherChannels.includes(channelName)) {
|
|
setupPusher(pusherKey, {
|
|
channelId: channelName,
|
|
messageCreated: this.receiveNewMessage,
|
|
messageDeleted: this.removeMessage,
|
|
messageEdited: this.updateMessage,
|
|
channelCleared: this.clearChannel,
|
|
redactUserMessages: this.redactUserMessages,
|
|
channelError: this.channelError,
|
|
mentioned: this.mentioned,
|
|
messageOpened: this.messageOpened,
|
|
});
|
|
const subscriptions = subscribedPusherChannels;
|
|
subscriptions.push(channelName);
|
|
this.setState({ subscribedPusherChannels: subscriptions });
|
|
}
|
|
};
|
|
|
|
mentioned = () => {};
|
|
|
|
messageOpened = () => {};
|
|
|
|
loadChannels = (channels, query) => {
|
|
const { activeChannelId, appDomain } = this.state;
|
|
const activeChannel =
|
|
this.state.activeChannel ||
|
|
channels.filter(
|
|
(channel) => channel.chat_channel_id === activeChannelId,
|
|
)[0];
|
|
if (activeChannelId && query.length === 0) {
|
|
this.setState({
|
|
chatChannels: channels,
|
|
scrolled: false,
|
|
channelsLoaded: true,
|
|
channelPaginationNum: 0,
|
|
filterQuery: '',
|
|
activeChannel:
|
|
activeChannel ||
|
|
this.filterForActiveChannel(channels, activeChannelId),
|
|
});
|
|
this.setupChannel(activeChannelId);
|
|
} else if (activeChannelId) {
|
|
this.setState({
|
|
scrolled: false,
|
|
chatChannels: channels,
|
|
channelsLoaded: true,
|
|
channelPaginationNum: 0,
|
|
filterQuery: query,
|
|
activeChannel:
|
|
activeChannel ||
|
|
this.filterForActiveChannel(channels, activeChannelId),
|
|
});
|
|
this.setupChannel(activeChannelId);
|
|
} else if (channels.length > 0) {
|
|
this.setState({
|
|
chatChannels: channels,
|
|
channelsLoaded: true,
|
|
channelPaginationNum: 0,
|
|
filterQuery: query || '',
|
|
scrolled: false,
|
|
});
|
|
|
|
this.triggerSwitchChannel(
|
|
channels[0].chat_channel_id,
|
|
channels[0].channel_modified_slug,
|
|
channels,
|
|
);
|
|
} else {
|
|
this.setState({ channelsLoaded: true });
|
|
}
|
|
this.subscribeChannelsToPusher(
|
|
channels.filter(this.channelTypeFilterFn('open')),
|
|
(channel) => `open-channel--${appDomain}-${channel.chat_channel_id}`,
|
|
);
|
|
this.subscribeChannelsToPusher(
|
|
channels.filter(this.channelTypeFilterFn('invite_only')),
|
|
(channel) => `private-channel--${appDomain}-${channel.chat_channel_id}`,
|
|
);
|
|
const chatChannelsList = document.getElementById(
|
|
'chatchannels__channelslist',
|
|
);
|
|
|
|
if (chatChannelsList) {
|
|
chatChannelsList.scrollTop = 0;
|
|
}
|
|
};
|
|
|
|
markUnopenedChannelIds = (ids) => {
|
|
this.setState({ unopenedChannelIds: ids });
|
|
};
|
|
|
|
subscribeChannelsToPusher = (channels, channelNameFn) => {
|
|
channels.forEach((channel) => {
|
|
this.subscribePusher(channelNameFn(channel));
|
|
});
|
|
};
|
|
|
|
channelTypeFilterFn = (type) => (channel) => {
|
|
return channel.channel_type === type;
|
|
};
|
|
|
|
setupChannels = (channels) => {
|
|
channels.forEach((channel, index) => {
|
|
if (index < 3) {
|
|
this.setupChannel(channel.chat_channel_id);
|
|
}
|
|
});
|
|
};
|
|
|
|
loadPaginatedChannels = (channels) => {
|
|
const { state } = this;
|
|
const currentChannels = state.chatChannels;
|
|
const currentChannelIds = currentChannels.map((channel) => channel.id);
|
|
const newChannels = currentChannels;
|
|
channels.forEach((channel) => {
|
|
if (!currentChannelIds.includes(channel.id)) {
|
|
newChannels.push(channel);
|
|
}
|
|
});
|
|
if (
|
|
currentChannels.length === newChannels.length &&
|
|
state.channelPaginationNum > 3
|
|
) {
|
|
return;
|
|
}
|
|
this.setState({
|
|
chatChannels: newChannels,
|
|
fetchingPaginatedChannels: false,
|
|
channelPaginationNum: state.channelPaginationNum + 1,
|
|
});
|
|
};
|
|
|
|
setupChannel = (channelId) => {
|
|
const { messages, messageOffset, activeChannel, appDomain } = this.state;
|
|
if (
|
|
!messages[channelId] ||
|
|
messages[channelId].length === 0 ||
|
|
messages[channelId][0].reception_method === 'pushed'
|
|
) {
|
|
getAllMessages(channelId, messageOffset, this.receiveAllMessages);
|
|
}
|
|
if (activeChannel && activeChannel.channel_type !== 'direct') {
|
|
getContent(
|
|
`/chat_channels/${channelId}/channel_info`,
|
|
this.setOpenChannelUsers,
|
|
null,
|
|
);
|
|
if (activeChannel.channel_type === 'open')
|
|
this.subscribePusher(`open-channel--${appDomain}-${channelId}`);
|
|
}
|
|
this.subscribePusher(`private-channel--${appDomain}-${channelId}`);
|
|
};
|
|
|
|
setOpenChannelUsers = (res) => {
|
|
const { activeChannelId, activeChannel } = this.state;
|
|
Object.filter = (obj, predicate) =>
|
|
Object.fromEntries(Object.entries(obj).filter(predicate));
|
|
const leftUser = Object.filter(
|
|
res.channel_users,
|
|
([username]) => username !== window.currentUser.username,
|
|
);
|
|
if (activeChannel && activeChannel.channel_type === 'open') {
|
|
this.setState({
|
|
channelUsers: {
|
|
[activeChannelId]: leftUser,
|
|
},
|
|
});
|
|
} else {
|
|
this.setState({
|
|
channelUsers: {
|
|
[activeChannelId]: {
|
|
all: { username: 'all', name: 'To notify everyone here' },
|
|
...leftUser,
|
|
},
|
|
},
|
|
});
|
|
}
|
|
};
|
|
|
|
observerCallback = (entries) => {
|
|
entries.forEach((entry) => {
|
|
if (entry.isIntersecting && this.state.scrolled === true) {
|
|
this.setState({ scrolled: false, showAlert: false });
|
|
} else if (this.state.scrolled === false) {
|
|
this.setState({
|
|
scrolled: true,
|
|
rerenderIfUnchangedCheck: Math.random(),
|
|
});
|
|
}
|
|
});
|
|
};
|
|
|
|
channelError = (_error) => {
|
|
this.setState({
|
|
subscribedPusherChannels: [],
|
|
});
|
|
};
|
|
|
|
receiveAllMessages = (res) => {
|
|
const { chatChannelId, messages } = res;
|
|
this.setState((prevState) => ({
|
|
messages: { ...prevState.messages, [chatChannelId]: messages },
|
|
scrolled: false,
|
|
}));
|
|
};
|
|
|
|
removeMessage = (message) => {
|
|
const { activeChannelId } = this.state;
|
|
this.setState((prevState) => ({
|
|
messages: {
|
|
[activeChannelId]: [
|
|
...prevState.messages[activeChannelId].filter(
|
|
(oldmessage) => oldmessage.id !== message.id,
|
|
),
|
|
],
|
|
},
|
|
}));
|
|
};
|
|
|
|
updateMessage = (message) => {
|
|
const { activeChannelId } = this.state;
|
|
if (message.chat_channel_id === activeChannelId) {
|
|
this.setState(({ messages }) => {
|
|
const newMessages = messages;
|
|
const foundIndex = messages[activeChannelId].findIndex(
|
|
(oldMessage) => oldMessage.id === message.id,
|
|
);
|
|
newMessages[activeChannelId][foundIndex] = message;
|
|
return { messages: newMessages };
|
|
});
|
|
}
|
|
};
|
|
|
|
receiveNewMessage = (message) => {
|
|
const {
|
|
messages,
|
|
activeChannelId,
|
|
scrolled,
|
|
chatChannels,
|
|
currentUserId,
|
|
unopenedChannelIds,
|
|
} = this.state;
|
|
|
|
const receivedChatChannelId = message.chat_channel_id;
|
|
const messageList = document.getElementById('messagelist');
|
|
let newMessages = [];
|
|
const nearBottom =
|
|
messageList.scrollTop + messageList.offsetHeight + 400 >
|
|
messageList.scrollHeight;
|
|
|
|
if (nearBottom) {
|
|
scrollToBottom();
|
|
}
|
|
|
|
// If I'm not sender and tab is not active
|
|
if (message.user_id !== currentUserId && document.hidden) {
|
|
notifyUser();
|
|
}
|
|
|
|
if (
|
|
message.temp_id &&
|
|
messages[receivedChatChannelId] &&
|
|
messages[receivedChatChannelId].findIndex(
|
|
(oldmessage) => oldmessage.temp_id === message.temp_id,
|
|
) > -1
|
|
) {
|
|
// Remove reduntant messages
|
|
return;
|
|
}
|
|
|
|
if (messages[receivedChatChannelId]) {
|
|
newMessages = messages[receivedChatChannelId].slice();
|
|
newMessages.push(message);
|
|
if (newMessages.length > 150) {
|
|
newMessages.shift();
|
|
}
|
|
}
|
|
|
|
// Show alert if message received and you have scrolled up
|
|
const newShowAlert =
|
|
activeChannelId === receivedChatChannelId
|
|
? { showAlert: !nearBottom }
|
|
: {};
|
|
|
|
let newMessageChannelIndex = 0;
|
|
let newMessageChannel = null;
|
|
const newChannelsObj = chatChannels.map((channel, index) => {
|
|
if (receivedChatChannelId === channel.chat_channel_id) {
|
|
newMessageChannelIndex = index;
|
|
newMessageChannel = channel;
|
|
return { ...channel, channel_last_message_at: new Date() };
|
|
}
|
|
return channel;
|
|
});
|
|
|
|
if (newMessageChannelIndex > 0) {
|
|
newChannelsObj.splice(newMessageChannelIndex, 1);
|
|
newChannelsObj.unshift(newMessageChannel);
|
|
}
|
|
|
|
// Mark messages read
|
|
if (receivedChatChannelId === activeChannelId) {
|
|
sendOpen(receivedChatChannelId, this.handleChannelOpenSuccess, null);
|
|
} else {
|
|
const newUnopenedChannels = unopenedChannelIds;
|
|
if (!unopenedChannelIds.includes(receivedChatChannelId)) {
|
|
newUnopenedChannels.push(receivedChatChannelId);
|
|
}
|
|
this.setState({
|
|
unopenedChannelIds: newUnopenedChannels,
|
|
});
|
|
}
|
|
|
|
// Updating the messages
|
|
this.setState((prevState) => ({
|
|
...newShowAlert,
|
|
chatChannels: newChannelsObj,
|
|
messages: {
|
|
...prevState.messages,
|
|
[receivedChatChannelId]: newMessages,
|
|
},
|
|
}));
|
|
};
|
|
|
|
redactUserMessages = (res) => {
|
|
const { messages } = this.state;
|
|
const newMessages = hideMessages(messages, res.userId);
|
|
this.setState({ messages: newMessages });
|
|
};
|
|
|
|
clearChannel = (res) => {
|
|
this.setState((prevState) => ({
|
|
messages: { ...prevState.messages, [res.chat_channel_id]: [] },
|
|
}));
|
|
};
|
|
|
|
handleChannelScroll = (e) => {
|
|
const {
|
|
fetchingPaginatedChannels,
|
|
chatChannels,
|
|
channelTypeFilter,
|
|
filterQuery,
|
|
activeChannelId,
|
|
channelPaginationNum,
|
|
} = this.state;
|
|
|
|
if (fetchingPaginatedChannels || chatChannels.length < 30) {
|
|
return;
|
|
}
|
|
const { target } = e;
|
|
if (target.scrollTop + target.offsetHeight + 1800 > target.scrollHeight) {
|
|
this.setState({ fetchingPaginatedChannels: true });
|
|
|
|
const filters =
|
|
channelTypeFilter === 'all'
|
|
? {}
|
|
: { filters: `channel_type:${channelTypeFilter}` };
|
|
const searchParams = {
|
|
query: filterQuery,
|
|
retrievalID: activeChannelId,
|
|
searchType: '',
|
|
paginationNumber: channelPaginationNum,
|
|
};
|
|
getChannels(searchParams, filters, this.loadPaginatedChannels);
|
|
}
|
|
};
|
|
|
|
handleKeyDown = (e) => {
|
|
const {
|
|
showMemberlist,
|
|
activeContent,
|
|
activeChannelId,
|
|
messages,
|
|
currentUserId,
|
|
} = this.state;
|
|
const enterPressed = e.keyCode === 13;
|
|
const leftPressed = e.keyCode === 37;
|
|
const rightPressed = e.keyCode === 39;
|
|
const escPressed = e.keyCode === 27;
|
|
const targetValue = e.target.value;
|
|
const messageIsEmpty = targetValue.length === 0;
|
|
const shiftPressed = e.shiftKey;
|
|
const upArrowPressed = e.keyCode === 38;
|
|
const deletePressed = e.keyCode === 46;
|
|
|
|
if (enterPressed) {
|
|
if (showMemberlist) {
|
|
e.preventDefault();
|
|
const selectedUser = document.getElementsByClassName(
|
|
'active__message__list',
|
|
)[0];
|
|
this.addUserName({ target: selectedUser });
|
|
} else if (messageIsEmpty) {
|
|
e.preventDefault();
|
|
} else if (!messageIsEmpty && !shiftPressed) {
|
|
e.preventDefault();
|
|
this.handleMessageSubmit(e.target.value);
|
|
}
|
|
}
|
|
if (e.target.value.includes('@')) {
|
|
if (e.keyCode === 40 || e.keyCode === 38) {
|
|
e.preventDefault();
|
|
}
|
|
}
|
|
if (
|
|
leftPressed &&
|
|
activeContent[activeChannelId] &&
|
|
e.target.value === '' &&
|
|
document.getElementById('activecontent-iframe')
|
|
) {
|
|
e.preventDefault();
|
|
try {
|
|
e.target.value = document.getElementById(
|
|
'activecontent-iframe',
|
|
).contentWindow.location.href;
|
|
} catch (err) {
|
|
e.target.value = activeContent[activeChannelId].path;
|
|
}
|
|
}
|
|
if (
|
|
rightPressed &&
|
|
!activeContent[activeChannelId] &&
|
|
e.target.value === ''
|
|
) {
|
|
e.preventDefault();
|
|
const richLinks = document.getElementsByClassName(
|
|
'chatchannels__richlink',
|
|
);
|
|
if (richLinks.length === 0) {
|
|
return;
|
|
}
|
|
this.setActiveContentState(activeChannelId, {
|
|
type_of: 'loading-post',
|
|
});
|
|
this.setActiveContent({
|
|
path: richLinks[richLinks.length - 1].href,
|
|
type_of: 'article',
|
|
});
|
|
}
|
|
if (escPressed && activeContent[activeChannelId]) {
|
|
this.setActiveContentState(activeChannelId, null);
|
|
this.setState({
|
|
fullscreenContent: null,
|
|
expanded: window.innerWidth > NARROW_WIDTH_LIMIT,
|
|
});
|
|
}
|
|
if (messageIsEmpty) {
|
|
const messagesByCurrentUser = messages[activeChannelId].filter(
|
|
(message) => message.user_id === currentUserId,
|
|
);
|
|
const lastMessage =
|
|
messagesByCurrentUser[messagesByCurrentUser.length - 1];
|
|
|
|
if (lastMessage) {
|
|
if (upArrowPressed) {
|
|
this.triggerEditMessage(lastMessage.id);
|
|
} else if (deletePressed) {
|
|
this.triggerDeleteMessage(lastMessage.id);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
handleKeyDownEdit = (e) => {
|
|
const enterPressed = e.keyCode === 13;
|
|
const targetValue = e.target.value;
|
|
const messageIsEmpty = targetValue.length === 0;
|
|
const shiftPressed = e.shiftKey;
|
|
|
|
if (enterPressed) {
|
|
if (messageIsEmpty) {
|
|
e.preventDefault();
|
|
} else if (!messageIsEmpty && !shiftPressed) {
|
|
e.preventDefault();
|
|
this.handleMessageSubmitEdit(e.target.value);
|
|
}
|
|
}
|
|
};
|
|
|
|
handleMessageSubmitEdit = (message) => {
|
|
const { activeChannelId, activeEditMessage } = this.state;
|
|
const editedMessage = {
|
|
activeChannelId,
|
|
id: activeEditMessage.id,
|
|
message,
|
|
};
|
|
editMessage(editedMessage, this.handleSuccess, this.handleFailure);
|
|
this.handleEditMessageClose();
|
|
};
|
|
|
|
handleMessageSubmit = (message) => {
|
|
const { activeChannelId } = this.state;
|
|
scrollToBottom();
|
|
// should check if user has the privilege
|
|
if (message.startsWith('/code')) {
|
|
this.setActiveContentState(activeChannelId, { type_of: 'code_editor' });
|
|
} else if (message.startsWith('/call')) {
|
|
const messageObject = {
|
|
activeChannelId,
|
|
message: '/call',
|
|
mentionedUsersId: this.getMentionedUsers(message),
|
|
};
|
|
this.setState({ videoPath: `/video_chats/${activeChannelId}` });
|
|
sendMessage(messageObject, this.handleSuccess, this.handleFailure);
|
|
} else if (message.startsWith('/play ')) {
|
|
const messageObject = {
|
|
activeChannelId,
|
|
message,
|
|
mentionedUsersId: this.getMentionedUsers(message),
|
|
};
|
|
sendMessage(messageObject, this.handleSuccess, this.handleFailure);
|
|
} else if (message.startsWith('/new')) {
|
|
this.setActiveContentState(activeChannelId, {
|
|
type_of: 'loading-post',
|
|
});
|
|
this.setActiveContent({
|
|
path: '/new',
|
|
type_of: 'article',
|
|
});
|
|
} else if (message.startsWith('/search')) {
|
|
this.setActiveContentState(activeChannelId, {
|
|
type_of: 'loading-post',
|
|
});
|
|
this.setActiveContent({
|
|
path: `/search?q=${message.replace('/search ', '')}`,
|
|
type_of: 'article',
|
|
});
|
|
} else if (message.startsWith('/s ')) {
|
|
this.setActiveContentState(activeChannelId, {
|
|
type_of: 'loading-post',
|
|
});
|
|
this.setActiveContent({
|
|
path: `/search?q=${message.replace('/s ', '')}`,
|
|
type_of: 'article',
|
|
});
|
|
} else if (message.startsWith('/ban ') || message.startsWith('/unban ')) {
|
|
conductModeration(
|
|
activeChannelId,
|
|
message,
|
|
this.handleSuccess,
|
|
this.handleFailure,
|
|
);
|
|
} else if (message.startsWith('/draw')) {
|
|
this.setActiveContent({
|
|
sendCanvasImage: this.sendCanvasImage,
|
|
type_of: 'draw',
|
|
});
|
|
} else if (message.startsWith('/')) {
|
|
this.setActiveContentState(activeChannelId, {
|
|
type_of: 'loading-post',
|
|
});
|
|
this.setActiveContent({
|
|
path: message,
|
|
type_of: 'article',
|
|
});
|
|
} else if (message.startsWith('/github')) {
|
|
const args = message.split('/github ')[1].trim();
|
|
this.setActiveContentState(activeChannelId, { type_of: 'github', args });
|
|
} else {
|
|
const messageObject = {
|
|
activeChannelId,
|
|
message,
|
|
mentionedUsersId: this.getMentionedUsers(message),
|
|
};
|
|
this.setState({ scrolled: false, showAlert: false });
|
|
sendMessage(messageObject, this.handleSuccess, this.handleFailure);
|
|
}
|
|
};
|
|
hideChannelList = () => {
|
|
const chatContainer = document.getElementsByClassName(
|
|
'chat__activechat',
|
|
)[0];
|
|
chatContainer.classList.remove('chat__activechat--hidden');
|
|
};
|
|
handleSwitchChannel = (e) => {
|
|
e.preventDefault();
|
|
let { target } = e;
|
|
this.hideChannelList();
|
|
if (!target.dataset.channelId) {
|
|
target = target.parentElement;
|
|
}
|
|
this.triggerSwitchChannel(
|
|
parseInt(target.dataset.channelId, 10),
|
|
target.dataset.channelSlug,
|
|
);
|
|
};
|
|
|
|
triggerSwitchChannel = (id, slug, channels) => {
|
|
const {
|
|
chatChannels,
|
|
isMobileDevice,
|
|
unopenedChannelIds,
|
|
activeChannelId,
|
|
currentUserId,
|
|
} = this.state;
|
|
const channelList = channels || chatChannels;
|
|
const newUnopenedChannelIds = unopenedChannelIds;
|
|
const index = newUnopenedChannelIds.indexOf(id);
|
|
if (index > -1) {
|
|
newUnopenedChannelIds.splice(index, 1);
|
|
}
|
|
this.setState({
|
|
activeChannel: this.filterForActiveChannel(
|
|
channelList,
|
|
id,
|
|
currentUserId,
|
|
),
|
|
activeChannelId: parseInt(id, 10),
|
|
scrolled: false,
|
|
showAlert: false,
|
|
allMessagesLoaded: false,
|
|
unopenedChannelIds: unopenedChannelIds.filter(
|
|
(unopenedId) => unopenedId !== id,
|
|
),
|
|
});
|
|
this.setupChannel(id);
|
|
const params = new URLSearchParams(window.location.search);
|
|
|
|
if (params.get('ref') === 'group_invite') {
|
|
this.setActiveContentState(activeChannelId, {
|
|
type_of: 'loading-post',
|
|
});
|
|
this.setActiveContent({
|
|
path: '/chat_channel_memberships',
|
|
type_of: 'article',
|
|
});
|
|
}
|
|
window.history.replaceState(null, null, `/connect/${slug}`);
|
|
if (!isMobileDevice) {
|
|
document.getElementById('messageform').focus();
|
|
}
|
|
if (window.ga && ga.create) {
|
|
ga('send', 'pageview', window.location.pathname + window.location.search);
|
|
}
|
|
sendOpen(id, this.handleChannelOpenSuccess, null);
|
|
};
|
|
|
|
handleSubmitOnClick = (e) => {
|
|
e.preventDefault();
|
|
const message = document.getElementById('messageform').value;
|
|
if (message.length > 0) {
|
|
this.handleMessageSubmit(message);
|
|
}
|
|
};
|
|
|
|
handleSubmitOnClickEdit = (e) => {
|
|
e.preventDefault();
|
|
const message = document.getElementById('messageform').value;
|
|
if (message.length > 0) {
|
|
this.handleMessageSubmitEdit(message);
|
|
}
|
|
};
|
|
|
|
triggerDeleteMessage = (messageId) => {
|
|
this.setState({ messageDeleteId: messageId });
|
|
this.setState({ showDeleteModal: true });
|
|
};
|
|
|
|
triggerEditMessage = (messageId) => {
|
|
const { messages, activeChannelId } = this.state;
|
|
this.setState({
|
|
activeEditMessage: messages[activeChannelId].filter(
|
|
(message) => message.id === messageId,
|
|
)[0],
|
|
});
|
|
this.setState({ startEditing: true });
|
|
};
|
|
|
|
handleSuccess = (response) => {
|
|
const { activeChannelId } = this.state;
|
|
scrollToBottom();
|
|
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,
|
|
);
|
|
if (foundIndex > 0) {
|
|
newMessages[activeChannelId][foundIndex].id = response.message.id;
|
|
}
|
|
return { messages: newMessages };
|
|
});
|
|
}
|
|
} else if (response.status === 'moderation-success') {
|
|
addSnackbarItem({ message: response.message, addCloseButton: true });
|
|
} else if (response.status === 'error') {
|
|
addSnackbarItem({ message: response.message, addCloseButton: true });
|
|
}
|
|
};
|
|
|
|
handleRequestRejection = (e) => {
|
|
rejectJoiningRequest(
|
|
e.target.dataset.channelId,
|
|
e.target.dataset.membershipId,
|
|
this.handleJoiningManagerSuccess(e.target.dataset.membershipId),
|
|
null,
|
|
);
|
|
};
|
|
|
|
handleRequestApproval = (e) => {
|
|
acceptJoiningRequest(
|
|
e.target.dataset.channelId,
|
|
e.target.dataset.membershipId,
|
|
this.handleJoiningManagerSuccess(e.target.dataset.membershipId),
|
|
null,
|
|
);
|
|
};
|
|
|
|
handleUpdateRequestCount = (isAccepted = false, acceptedInfo) => {
|
|
if (isAccepted) {
|
|
const searchParams = {
|
|
query: '',
|
|
retrievalID: null,
|
|
searchType: '',
|
|
paginationNumber: 0,
|
|
};
|
|
getChannels(searchParams, 'all', this.loadChannels);
|
|
this.triggerSwitchChannel(
|
|
parseInt(acceptedInfo.channelId, 10),
|
|
acceptedInfo.channelSlug,
|
|
this.state.chatChannels,
|
|
);
|
|
}
|
|
|
|
this.setState((prevState) => {
|
|
return {
|
|
userRequestCount: prevState.userRequestCount - 1,
|
|
};
|
|
});
|
|
};
|
|
|
|
triggerActiveContent = (e) => {
|
|
if (
|
|
// Trying to open in new tab
|
|
e.ctrlKey ||
|
|
e.shiftKey ||
|
|
e.metaKey || // apple
|
|
(e.button && e.button === 1) // middle click, >IE9 + everyone else
|
|
) {
|
|
return false;
|
|
}
|
|
const { target } = e;
|
|
const content =
|
|
target.dataset.content || target.parentElement.dataset.content;
|
|
if (content) {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
this.hideChannelList();
|
|
|
|
const { activeChannelId, activeChannel } = this.state;
|
|
|
|
if (content.startsWith('chat_channels/')) {
|
|
this.setActiveContentState(activeChannelId, {
|
|
type_of: 'loading-user',
|
|
});
|
|
getContent(`/${content}/channel_info`, this.setActiveContent, null);
|
|
} else if (content === 'sidecar-channel-request') {
|
|
this.setActiveContent({
|
|
data: {
|
|
user: getCurrentUser(),
|
|
channel: {
|
|
id: target.dataset.channelId,
|
|
name: target.dataset.channelName,
|
|
status: target.dataset.channelStatus,
|
|
},
|
|
},
|
|
handleJoiningRequest: this.handleJoiningRequest,
|
|
type_of: 'channel-request',
|
|
});
|
|
} else if (content === 'sidecar-joining-request-manager') {
|
|
this.setActiveContent({
|
|
data: {},
|
|
type_of: 'channel-request-manager',
|
|
updateRequestCount: this.handleUpdateRequestCount,
|
|
});
|
|
} else if (content === 'sidecar_all') {
|
|
this.setActiveContentState(activeChannelId, {
|
|
type_of: 'loading-post',
|
|
});
|
|
this.setActiveContent({
|
|
path: `/chat_channel_memberships/${activeChannel.id}/edit`,
|
|
type_of: 'article',
|
|
});
|
|
} else if (content.startsWith('sidecar-content-plus-video')) {
|
|
this.setActiveContentState(activeChannelId, {
|
|
type_of: 'loading-post',
|
|
});
|
|
this.setActiveContent({
|
|
path: target.href || target.parentElement.href,
|
|
type_of: 'article',
|
|
});
|
|
this.setState({ videoPath: `/video_chats/${activeChannelId}` });
|
|
} else if (content.startsWith('sidecar-video')) {
|
|
this.setState({ videoPath: target.href || target.parentElement.href });
|
|
} else if (
|
|
content.startsWith('sidecar') ||
|
|
content.startsWith('article')
|
|
) {
|
|
// article is legacy which can be removed shortly
|
|
this.setActiveContentState(activeChannelId, {
|
|
type_of: 'loading-post',
|
|
});
|
|
this.setActiveContent({
|
|
path: target.href || target.parentElement.href,
|
|
type_of: 'article',
|
|
});
|
|
} else if (target.dataset.content === 'exit') {
|
|
this.setActiveContentState(activeChannelId, null);
|
|
this.setState({
|
|
fullscreenContent: null,
|
|
expanded: window.innerWidth > NARROW_WIDTH_LIMIT,
|
|
});
|
|
} else if (target.dataset.content === 'fullscreen') {
|
|
const mode =
|
|
this.state.fullscreenContent === 'sidecar' ? null : 'sidecar';
|
|
this.setState({
|
|
fullscreenContent: mode,
|
|
expanded: mode === null || window.innerWidth > WIDE_WIDTH_LIMIT,
|
|
});
|
|
} else if (target.dataset.content === 'chat_channel_setting') {
|
|
this.setActiveContent({
|
|
data: {},
|
|
type_of: 'chat-channel-setting',
|
|
activeMembershipId: activeChannel.id,
|
|
handleLeavingChannel: this.handleLeavingChannel,
|
|
});
|
|
}
|
|
}
|
|
return false;
|
|
};
|
|
|
|
setActiveContentState = (channelId, state) => {
|
|
this.setState((prevState) => ({
|
|
activeContent: {
|
|
...prevState.activeContent,
|
|
[channelId]: state,
|
|
},
|
|
}));
|
|
};
|
|
|
|
setActiveContent = (response) => {
|
|
const { activeChannelId } = this.state;
|
|
this.setActiveContentState(activeChannelId, response);
|
|
setTimeout(() => {
|
|
document.getElementById('chat_activecontent').scrollTop = 0;
|
|
document.getElementById('chat').scrollLeft = 1000;
|
|
}, 3);
|
|
setTimeout(() => {
|
|
document.getElementById('chat_activecontent').scrollTop = 0;
|
|
document.getElementById('chat').scrollLeft = 1000;
|
|
}, 10);
|
|
};
|
|
|
|
handleChannelOpenSuccess = (response) => {
|
|
this.setState(({ chatChannels }) => {
|
|
const newChannelsObj = chatChannels.map((channel) => {
|
|
if (parseInt(response.channel, 10) === channel.chat_channel_id) {
|
|
return { ...channel, last_opened_at: new Date() };
|
|
}
|
|
return channel;
|
|
});
|
|
return { chatChannels: newChannelsObj };
|
|
});
|
|
};
|
|
|
|
handleLeavingChannel = (leftChannelId) => {
|
|
const { chatChannels } = this.state;
|
|
this.triggerSwitchChannel(
|
|
chatChannels[1].chat_channel_id,
|
|
chatChannels[1].channel_modified_slug,
|
|
chatChannels,
|
|
);
|
|
this.setState((prevState) => ({
|
|
chatChannels: prevState.chatChannels.filter(
|
|
(channel) => channel.id !== leftChannelId,
|
|
),
|
|
}));
|
|
this.setActiveContentState(chatChannels[1].chat_channel_id, null);
|
|
};
|
|
|
|
triggerChannelTypeFilter = (e) => {
|
|
const { filterQuery } = this.state;
|
|
const type = e.target.dataset.channelType;
|
|
this.setState({
|
|
channelTypeFilter: type,
|
|
fetchingPaginatedChannels: false,
|
|
});
|
|
const filters = type === 'all' ? {} : { filters: `channel_type:${type}` };
|
|
const searchParams = {
|
|
query: filterQuery,
|
|
retrievalID: null,
|
|
searchType: '',
|
|
paginationNumber: 0,
|
|
};
|
|
if (filterQuery && type !== 'direct') {
|
|
searchParams.searchType = 'discoverable';
|
|
getChannels(searchParams, filters, this.loadChannels);
|
|
} else {
|
|
getChannels(searchParams, filters, this.loadChannels);
|
|
}
|
|
};
|
|
|
|
handleFailure = (err) => {
|
|
// eslint-disable-next-line no-console
|
|
console.error(err);
|
|
addSnackbarItem({ message: err, addCloseButton: true });
|
|
};
|
|
|
|
renderMessages = () => {
|
|
const {
|
|
activeChannelId,
|
|
messages,
|
|
showTimestamp,
|
|
activeChannel,
|
|
currentUserId,
|
|
} = this.state;
|
|
if (!messages[activeChannelId]) {
|
|
return '';
|
|
}
|
|
if (messages[activeChannelId].length === 0 && activeChannel) {
|
|
if (activeChannel.channel_type === 'direct') {
|
|
return (
|
|
<div className="chatmessage" style={{ color: 'grey' }}>
|
|
<div className="chatmessage__body">
|
|
You and{' '}
|
|
<a href={`/${activeChannel.channel_modified_slug}`}>
|
|
{activeChannel.channel_modified_slug}
|
|
</a>{' '}
|
|
are connected because you both follow each other. All interactions{' '}
|
|
<em>
|
|
<b>must</b>
|
|
</em>{' '}
|
|
abide by the <a href="/code-of-conduct">code of conduct</a>.
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
if (activeChannel.channel_type === 'open') {
|
|
return (
|
|
<div className="chatmessage" style={{ color: 'grey' }}>
|
|
<div className="chatmessage__body">
|
|
You have joined {activeChannel.channel_name}! All interactions{' '}
|
|
<em>
|
|
<b>must</b>
|
|
</em>{' '}
|
|
abide by the <a href="/code-of-conduct">code of conduct</a>.
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
}
|
|
|
|
return messages[activeChannelId].map((message) =>
|
|
message.action ? (
|
|
<ActionMessage
|
|
user={message.username}
|
|
profileImageUrl={message.profile_image_url}
|
|
message={message.message}
|
|
timestamp={showTimestamp ? message.timestamp : null}
|
|
color={message.color}
|
|
onContentTrigger={this.triggerActiveContent}
|
|
/>
|
|
) : (
|
|
<Message
|
|
currentUserId={currentUserId}
|
|
id={message.id}
|
|
user={message.username}
|
|
userID={message.user_id}
|
|
profileImageUrl={message.profile_image_url}
|
|
message={message.message}
|
|
timestamp={showTimestamp ? message.timestamp : null}
|
|
editedAt={message.edited_at}
|
|
color={message.color}
|
|
onContentTrigger={this.triggerActiveContent}
|
|
onDeleteMessageTrigger={this.triggerDeleteMessage}
|
|
onEditMessageTrigger={this.triggerEditMessage}
|
|
/>
|
|
),
|
|
);
|
|
};
|
|
|
|
triggerChannelFilter = (e) => {
|
|
const { channelTypeFilter } = this.state;
|
|
const filters =
|
|
channelTypeFilter === 'all'
|
|
? {}
|
|
: { filters: `channel_type:${channelTypeFilter}` };
|
|
const searchParams = {
|
|
query: e.target.value,
|
|
retrievalID: null,
|
|
searchType: '',
|
|
paginationNumber: 0,
|
|
};
|
|
if (e.target.value) {
|
|
searchParams.searchType = 'discoverable';
|
|
getChannels(searchParams, filters, this.loadChannels);
|
|
} else {
|
|
getChannels(searchParams, filters, this.loadChannels);
|
|
}
|
|
};
|
|
|
|
toggleExpand = () => {
|
|
this.setState((prevState) => ({ expanded: !prevState.expanded }));
|
|
};
|
|
|
|
renderChannelFilterButton = (type, name, active) => (
|
|
<Button
|
|
data-channel-type={type}
|
|
onClick={this.triggerChannelTypeFilter}
|
|
className={`chat__channeltypefilterbutton crayons-indicator crayons-indicator--${
|
|
type === active ? 'accent' : ''
|
|
}`}
|
|
>
|
|
{name}
|
|
</Button>
|
|
);
|
|
|
|
toggleSearchShowing = () => {
|
|
if (!this.state.searchShowing) {
|
|
setTimeout(() => {
|
|
document.getElementById('chatchannelsearchbar').focus();
|
|
}, 100);
|
|
} else {
|
|
const searchParams = {
|
|
query: '',
|
|
retrievalID: null,
|
|
searchType: '',
|
|
paginationNumber: 0,
|
|
};
|
|
getChannels(searchParams, 'all', this.loadChannels);
|
|
this.setState({ filterQuery: '' });
|
|
}
|
|
this.setState({ searchShowing: !this.state.searchShowing });
|
|
};
|
|
|
|
renderChatChannels = () => {
|
|
const { state } = this;
|
|
if (state.showChannelsList) {
|
|
const { notificationsPermission } = state;
|
|
const notificationsButton = '';
|
|
let notificationsState = '';
|
|
let invitesButton = '';
|
|
let joiningRequestButton = '';
|
|
if (notificationsPermission === 'granted') {
|
|
notificationsState = (
|
|
<div className="chat_chatconfig chat_chatconfig--on">
|
|
Notifications On
|
|
</div>
|
|
);
|
|
} else if (notificationsPermission === 'denied') {
|
|
notificationsState = (
|
|
<div className="chat_chatconfig chat_chatconfig--off">
|
|
Notifications Off
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div className="chat__channels">
|
|
{notificationsButton}
|
|
|
|
<input
|
|
placeholder="Search Channels"
|
|
onKeyUp={this.debouncedChannelFilter}
|
|
id="chatchannelsearchbar"
|
|
className="crayons-textfield"
|
|
aria-label="Search Channels"
|
|
/>
|
|
{invitesButton}
|
|
{joiningRequestButton}
|
|
<div className="chat__channeltypefilter">
|
|
{this.renderChannelFilterButton(
|
|
'all',
|
|
'all',
|
|
state.channelTypeFilter,
|
|
)}
|
|
{this.renderChannelFilterButton(
|
|
'direct',
|
|
'direct',
|
|
state.channelTypeFilter,
|
|
)}
|
|
{this.renderChannelFilterButton(
|
|
'invite_only',
|
|
'group',
|
|
state.channelTypeFilter,
|
|
)}
|
|
<Button
|
|
className="chat__channelssearchtoggle crayons-btn--ghost-dimmed p-2"
|
|
aria-label="Toggle request manager"
|
|
onClick={this.triggerActiveContent}
|
|
data-content="sidecar-joining-request-manager"
|
|
>
|
|
<svg
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
viewBox="0 0 477.869 477.869"
|
|
width="18"
|
|
height="18"
|
|
>
|
|
<path d="M387.415 233.496c48.976-44.03 52.987-119.424 8.958-168.4C355.99 20.177 288.4 12.546 239.02 47.332c-53.83-38-128.264-25.15-166.254 28.68-34.86 49.393-27.26 117.054 17.69 157.483C34.606 262.935-.25 320.976.002 384.108v51.2a17.07 17.07 0 0 0 17.067 17.067h443.733a17.07 17.07 0 0 0 17.067-17.067v-51.2c.252-63.132-34.605-121.173-90.454-150.612zM307.2 59.842c47.062-.052 85.256 38.057 85.31 85.12.037 33.564-19.63 64.023-50.237 77.8-1.314.597-2.628 1.143-3.96 1.707a83.66 83.66 0 0 1-12.988 4.045c-.853.188-1.707.3-2.577.46-4.952.95-9.977 1.457-15.02 1.52-2.27 0-4.557-.17-6.827-.375-.853 0-1.707 0-2.56-.17a86.22 86.22 0 0 1-27.904-8.226c-.324-.154-.7-.137-1.024-.273-1.707-.82-3.413-1.536-4.932-2.458.137-.17.222-.358.358-.53a119.72 119.72 0 0 0 18.278-33.297l.53-1.434a120.38 120.38 0 0 0 4.523-17.562c.154-.87.273-1.707.4-2.645.987-6.067 1.506-12.2 1.553-18.347a120.04 120.04 0 0 0-1.553-18.313l-.4-2.645c-1.064-5.96-2.576-11.83-4.523-17.562l-.53-1.434c-4.282-12-10.453-23.24-18.278-33.297-.137-.17-.222-.358-.358-.53C277.45 63.83 292.2 59.843 307.2 59.842zM85.335 145.176c-.12-47.006 37.886-85.2 84.892-85.33a85.11 85.11 0 0 1 59.134 23.686l2.918 2.9a87.75 87.75 0 0 1 8.09 9.813c.75 1.058 1.434 2.185 2.133 3.277a83.95 83.95 0 0 1 6.263 11.52c.427.973.75 1.963 1.126 2.935a83.42 83.42 0 0 1 4.233 13.653c.12.512.154 1.024.256 1.553a80.34 80.34 0 0 1 0 32.119c-.102.53-.137 1.04-.256 1.553a83.23 83.23 0 0 1-4.233 13.653c-.375.973-.7 1.963-1.126 2.935a84.25 84.25 0 0 1-6.263 11.503c-.7 1.092-1.382 2.22-2.133 3.277a87.55 87.55 0 0 1-8.09 9.813 117.37 117.37 0 0 1-2.918 2.901c-6.9 6.585-14.877 11.962-23.57 15.906a49.35 49.35 0 0 1-4.198 1.707 85.84 85.84 0 0 1-12.663 3.925c-1.075.24-2.185.375-3.277.563a84.67 84.67 0 0 1-14.046 1.417h-1.877c-4.713-.08-9.412-.554-14.046-1.417-1.092-.188-2.202-.324-3.277-.563a85.8 85.8 0 0 1-12.663-3.925l-4.198-1.707c-30.534-13.786-50.173-44.166-50.212-77.667zM307.2 418.242H34.135V384.11c-.25-57.833 36.188-109.468 90.76-128.614 29.296 12.197 62.25 12.197 91.546 0a137.14 137.14 0 0 1 16.623 7.356c3.55 1.826 6.827 3.908 10.24 6.007 2.22 1.382 4.47 2.73 6.605 4.25 3.294 2.338 6.4 4.88 9.455 7.492l5.75 5.12c2.816 2.662 5.46 5.478 8.004 8.363 1.826 2.082 3.6 4.198 5.29 6.383 2.236 2.867 4.37 5.803 6.35 8.823 1.707 2.56 3.226 5.222 4.727 7.885 1.707 2.935 3.277 5.87 4.7 8.926s2.697 6.4 3.925 9.66c1.075 2.833 2.22 5.65 3.106 8.533 1.195 3.96 2.03 8.055 2.867 12.15.512 2.423 1.178 4.796 1.553 7.253 1.01 6.757 1.53 13.58 1.553 20.412v34.133zm136.534 0h-102.4V384.11c0-5.342-.307-10.633-.785-15.872-.137-1.536-.375-3.055-.546-4.59-.46-3.772-1-7.51-1.707-11.213l-.973-4.762c-.82-3.8-1.77-7.566-2.85-11.298l-1.058-3.686c-4.78-15.277-11.704-29.797-20.565-43.127l-.666-.973a168.96 168.96 0 0 0-9.404-12.646l-.12-.154c-3.413-4.232-7.117-8.346-11.008-12.237h.7a120.8 120.8 0 0 0 14.524 1.024h.94c4.496-.04 8.985-.33 13.45-.87 1.4-.17 2.782-.427 4.18-.65a117.43 117.43 0 0 0 10.752-2.167l3.055-.785a116.21 116.21 0 0 0 13.653-4.642c54.612 19.127 91.083 70.785 90.83 128.65v34.132z" />
|
|
</svg>
|
|
{this.state.userRequestCount > 0 ? (
|
|
<span className="crayons-indicator crayons-indicator--accent crayons-indicator--bullet requests-badge">
|
|
{this.state.userRequestCount}
|
|
</span>
|
|
) : null}
|
|
</Button>
|
|
{this.state.isTagModerator ? (
|
|
<Button
|
|
className="chat__channelssearchtoggle crayons-btn--ghost-dimmed p-2"
|
|
aria-label="Toggle request manager"
|
|
onClick={this.toggleModalCreateChannel}
|
|
data-content="sidecar-joining-request-manager"
|
|
>
|
|
<svg
|
|
version="1.1"
|
|
id="Capa_1"
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
x="0px"
|
|
y="0px"
|
|
width="18"
|
|
height="18"
|
|
viewBox="0 0 512 512"
|
|
style="enable-background:new 0 0 512 512;"
|
|
>
|
|
<path
|
|
d="M492,236H276V20c0-11.046-8.954-20-20-20c-11.046,0-20,8.954-20,20v216H20c-11.046,0-20,8.954-20,20s8.954,20,20,20h216
|
|
v216c0,11.046,8.954,20,20,20s20-8.954,20-20V276h216c11.046,0,20-8.954,20-20C512,244.954,503.046,236,492,236z"
|
|
/>
|
|
</svg>
|
|
</Button>
|
|
) : null}
|
|
{this.state.openModal ? (
|
|
<CreateChatModal
|
|
toggleModalCreateChannel={this.toggleModalCreateChannel}
|
|
handleCreateChannelSuccess={this.handleCreateChannelSuccess}
|
|
/>
|
|
) : (
|
|
''
|
|
)}
|
|
</div>
|
|
<Channels
|
|
activeChannelId={state.activeChannelId}
|
|
chatChannels={state.chatChannels}
|
|
unopenedChannelIds={state.unopenedChannelIds}
|
|
handleSwitchChannel={this.handleSwitchChannel}
|
|
channelsLoaded={state.channelsLoaded}
|
|
filterQuery={state.filterQuery}
|
|
expanded={state.expanded}
|
|
aria-expanded={state.expanded}
|
|
currentUserId={state.currentUserId}
|
|
triggerActiveContent={this.triggerActiveContent}
|
|
/>
|
|
{notificationsState}
|
|
</div>
|
|
);
|
|
}
|
|
return '';
|
|
};
|
|
|
|
toggleModalCreateChannel = () => {
|
|
const { openModal } = this.state;
|
|
this.setState({ openModal: !openModal });
|
|
};
|
|
|
|
navigateToChannelsList = () => {
|
|
const chatContainer = document.getElementsByClassName(
|
|
'chat__activechat',
|
|
)[0];
|
|
|
|
chatContainer.classList.add('chat__activechat--hidden');
|
|
};
|
|
|
|
handleCreateChannelSuccess = () => {
|
|
this.toggleModalCreateChannel();
|
|
const searchParams = {
|
|
query: '',
|
|
retrievalID: null,
|
|
searchType: '',
|
|
paginationNumber: 0,
|
|
};
|
|
getChannels(searchParams, {}, this.loadChannels);
|
|
};
|
|
|
|
handleMessageScroll = () => {
|
|
const {
|
|
allMessagesLoaded,
|
|
messages,
|
|
activeChannelId,
|
|
messageOffset,
|
|
} = this.state;
|
|
|
|
if (!messages[activeChannelId]) {
|
|
return;
|
|
}
|
|
|
|
const jumpbackButton = document.getElementById('jumpback_button');
|
|
|
|
if (this.scroller) {
|
|
const scrolledRatio =
|
|
(this.scroller.scrollTop + this.scroller.clientHeight) /
|
|
this.scroller.scrollHeight;
|
|
|
|
if (scrolledRatio < 0.5) {
|
|
jumpbackButton.classList.remove('chatchanneljumpback__hide');
|
|
} else if (scrolledRatio > 0.6) {
|
|
jumpbackButton.classList.add('chatchanneljumpback__hide');
|
|
}
|
|
|
|
if (this.scroller.scrollTop === 0 && !allMessagesLoaded) {
|
|
getAllMessages(
|
|
activeChannelId,
|
|
messageOffset + messages[activeChannelId].length,
|
|
this.addMoreMessages,
|
|
);
|
|
const curretPosition = this.scroller.scrollHeight;
|
|
this.setState({ currentMessageLocation: curretPosition });
|
|
}
|
|
}
|
|
};
|
|
|
|
addMoreMessages = (res) => {
|
|
const { chatChannelId, messages } = res;
|
|
|
|
if (messages.length > 0) {
|
|
this.setState((prevState) => ({
|
|
messages: {
|
|
[chatChannelId]: [...messages, ...prevState.messages[chatChannelId]],
|
|
},
|
|
}));
|
|
} else {
|
|
this.setState({ allMessagesLoaded: true });
|
|
}
|
|
};
|
|
|
|
jumpBacktoBottom = () => {
|
|
scrollToBottom();
|
|
document
|
|
.getElementById('jumpback_button')
|
|
.classList.remove('chatchanneljumpback__hide');
|
|
};
|
|
|
|
handleDragOver = (event) => {
|
|
event.preventDefault();
|
|
event.currentTarget.classList.add('opacity-25');
|
|
};
|
|
|
|
handleDragExit = (event) => {
|
|
event.preventDefault();
|
|
event.currentTarget.classList.remove('opacity-25');
|
|
};
|
|
|
|
handleImageDrop = (event) => {
|
|
event.preventDefault();
|
|
const { files } = event.dataTransfer;
|
|
event.currentTarget.classList.remove('opacity-25');
|
|
processImageUpload(files, this.handleImageSuccess, this.handleImageFailure);
|
|
};
|
|
sendCanvasImage = (files) => {
|
|
dragAndUpload([files], this.handleImageSuccess, this.handleImageFailure);
|
|
};
|
|
handleImageSuccess = (res) => {
|
|
const { links, image } = res;
|
|
const mLink = `![${image[0].name}](${links[0]})`;
|
|
const el = document.getElementById('messageform');
|
|
const start = el.selectionStart;
|
|
const end = el.selectionEnd;
|
|
const text = el.value;
|
|
let before = text.substring(0, start);
|
|
before = text.substring(0, before.lastIndexOf('@') + 1);
|
|
const after = text.substring(end, text.length);
|
|
el.value = `${before + mLink} ${after}`;
|
|
el.selectionStart = start + mLink.length + 1;
|
|
el.selectionEnd = el.selectionStart;
|
|
el.focus();
|
|
};
|
|
handleImageFailure = (e) => {
|
|
addSnackbarItem({ message: e.message, addCloseButton: true });
|
|
};
|
|
handleDragHover(e) {
|
|
e.preventDefault();
|
|
const messageArea = document.getElementById('messagelist');
|
|
messageArea.classList.add('opacity-25');
|
|
}
|
|
handleDragExit(e) {
|
|
e.preventDefault();
|
|
const messageArea = document.getElementById('messagelist');
|
|
messageArea.classList.remove('opacity-25');
|
|
}
|
|
renderActiveChatChannel = (channelHeader) => {
|
|
const { state, props } = this;
|
|
const channelName = state.activeChannel
|
|
? state.activeChannel.channel_name
|
|
: ' ';
|
|
return (
|
|
<div className="activechatchannel">
|
|
<div className="activechatchannel__conversation">
|
|
{channelHeader}
|
|
<DragAndDropZone
|
|
onDragOver={this.handleDragOver}
|
|
onDragExit={this.handleDragExit}
|
|
onDrop={this.handleImageDrop}
|
|
>
|
|
<div
|
|
className="activechatchannel__messages"
|
|
onScroll={this.handleMessageScroll}
|
|
ref={(scroller) => {
|
|
this.scroller = scroller;
|
|
}}
|
|
id="messagelist"
|
|
>
|
|
{this.renderMessages()}
|
|
<div
|
|
className="messagelist__sentinel"
|
|
id="messagelist__sentinel"
|
|
/>
|
|
</div>
|
|
</DragAndDropZone>
|
|
<div
|
|
className="chatchanneljumpback chatchanneljumpback__hide"
|
|
id="jumpback_button"
|
|
>
|
|
<Button
|
|
className="chatchanneljumpback__messages crayons-btn--outlined"
|
|
onClick={this.jumpBacktoBottom}
|
|
tabIndex="0"
|
|
onKeyUp={(e) => {
|
|
if (e.keyCode === 13) this.jumpBacktoBottom();
|
|
}}
|
|
>
|
|
Scroll to Bottom
|
|
</Button>
|
|
</div>
|
|
{this.renderDeleteModal()}
|
|
<div className="activechatchannel__alerts">
|
|
<Alert showAlert={state.showAlert} />
|
|
</div>
|
|
{this.renderChannelMembersList()}
|
|
<div className="activechatchannel__form">
|
|
<Compose
|
|
handleSubmitOnClick={this.handleSubmitOnClick}
|
|
handleKeyDown={this.handleKeyDown}
|
|
handleSubmitOnClickEdit={this.handleSubmitOnClickEdit}
|
|
handleMention={this.handleMention}
|
|
handleKeyUp={this.handleKeyUp}
|
|
handleKeyDownEdit={this.handleKeyDownEdit}
|
|
activeChannelId={state.activeChannelId}
|
|
activeChannelName={channelName}
|
|
startEditing={state.startEditing}
|
|
markdownEdited={state.markdownEdited}
|
|
editMessageMarkdown={state.activeEditMessage.markdown}
|
|
handleEditMessageClose={this.handleEditMessageClose}
|
|
handleFilePaste={this.handleFilePaste}
|
|
/>
|
|
</div>
|
|
</div>
|
|
<Content
|
|
onTriggerContent={this.triggerActiveContent}
|
|
resource={state.activeContent[state.activeChannelId]}
|
|
activeChannel={state.activeChannel}
|
|
fullscreen={state.fullscreenContent === 'sidecar'}
|
|
/>
|
|
<VideoContent
|
|
videoPath={state.videoPath}
|
|
onTriggerVideoContent={this.onTriggerVideoContent}
|
|
fullscreen={state.fullscreenContent === 'video'}
|
|
/>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
handleFilePaste = (e) => {
|
|
if (!e.clipboardData || !e.clipboardData.items) {
|
|
return;
|
|
}
|
|
const items = [];
|
|
for (let i = 0; i < e.clipboardData.items.length; i++) {
|
|
const item = e.clipboardData.items[i];
|
|
if (item.kind !== 'file') {
|
|
continue;
|
|
}
|
|
items.push(item);
|
|
}
|
|
if (items && items.length > 0) {
|
|
processImageUpload(
|
|
[items[0].getAsFile()],
|
|
this.handleImageSuccess,
|
|
this.handleImageFailure,
|
|
);
|
|
}
|
|
};
|
|
|
|
onTriggerVideoContent = (e) => {
|
|
if (e.target.dataset.content === 'exit') {
|
|
this.setState({
|
|
videoPath: null,
|
|
fullscreenContent: null,
|
|
expanded: window.innerWidth > 600,
|
|
});
|
|
} else if (this.state.fullscreenContent === 'video') {
|
|
this.setState({ fullscreenContent: null });
|
|
} else {
|
|
this.setState({
|
|
fullscreenContent: 'video',
|
|
expanded: window.innerWidth > WIDE_WIDTH_LIMIT,
|
|
});
|
|
}
|
|
};
|
|
|
|
handleMention = (e) => {
|
|
const { activeChannel } = this.state;
|
|
const mention = e.keyCode === 64;
|
|
if (mention && activeChannel.channel_type !== 'direct') {
|
|
this.setState({ showMemberlist: true });
|
|
}
|
|
};
|
|
|
|
handleKeyUp = (e) => {
|
|
const { startEditing, activeChannel, showMemberlist } = this.state;
|
|
const enterPressed = e.keyCode === 13;
|
|
if (enterPressed && showMemberlist)
|
|
this.setState({ showMemberlist: false });
|
|
if (activeChannel.channel_type !== 'direct') {
|
|
if (startEditing) {
|
|
this.setState({ markdownEdited: true });
|
|
}
|
|
if (!e.target.value.includes('@') && showMemberlist) {
|
|
this.setState({ showMemberlist: false });
|
|
} else {
|
|
this.setQuery(e.target);
|
|
this.listHighlightManager(e.keyCode);
|
|
}
|
|
}
|
|
};
|
|
|
|
setQuery = (e) => {
|
|
const { showMemberlist } = this.state;
|
|
if (showMemberlist) {
|
|
const before = e.value.substring(0, e.selectionStart);
|
|
const query = before.substring(
|
|
before.lastIndexOf('@') + 1,
|
|
e.selectionStart,
|
|
);
|
|
|
|
if (query.includes(' ') || before.lastIndexOf('@') < 0)
|
|
this.setState({ showMemberlist: false });
|
|
else {
|
|
this.setState({ showMemberlist: true });
|
|
this.setState({ memberFilterQuery: query });
|
|
}
|
|
}
|
|
};
|
|
|
|
addUserName = (e) => {
|
|
const name =
|
|
e.target.dataset.content || e.target.parentElement.dataset.content;
|
|
const el = document.getElementById('messageform');
|
|
const start = el.selectionStart;
|
|
const end = el.selectionEnd;
|
|
const text = el.value;
|
|
let before = text.substring(0, start);
|
|
before = text.substring(0, before.lastIndexOf('@') + 1);
|
|
const after = text.substring(end, text.length);
|
|
el.value = `${before + name} ${after}`;
|
|
el.selectionStart = start + name.length + 1;
|
|
el.selectionEnd = start + name.length + 1;
|
|
el.focus();
|
|
this.setState({ showMemberlist: false });
|
|
};
|
|
|
|
listHighlightManager = (keyCode) => {
|
|
const mentionList = document.getElementById('mentionList');
|
|
const activeElement = document.getElementsByClassName(
|
|
'active__message__list',
|
|
)[0];
|
|
if (mentionList.children.length > 0) {
|
|
if (keyCode === 40 && activeElement) {
|
|
if (activeElement.nextElementSibling) {
|
|
activeElement.classList.remove('active__message__list');
|
|
activeElement.nextElementSibling.classList.add(
|
|
'active__message__list',
|
|
);
|
|
}
|
|
} else if (keyCode === 38 && activeElement) {
|
|
if (activeElement.previousElementSibling) {
|
|
activeElement.classList.remove('active__message__list');
|
|
activeElement.previousElementSibling.classList.add(
|
|
'active__message__list',
|
|
);
|
|
}
|
|
} else {
|
|
mentionList.children[0].classList.add('active__message__list');
|
|
}
|
|
}
|
|
};
|
|
|
|
getMentionedUsers = (message) => {
|
|
const { channelUsers, activeChannelId, activeChannel } = this.state;
|
|
if (channelUsers[activeChannelId]) {
|
|
if (message.includes('@all') && activeChannel.channel_type !== 'open') {
|
|
return Array.from(
|
|
Object.values(channelUsers[activeChannelId]).filter(
|
|
(user) => user.id,
|
|
),
|
|
(user) => user.id,
|
|
);
|
|
}
|
|
return Array.from(
|
|
Object.values(channelUsers[activeChannelId]).filter((user) =>
|
|
message.includes(user.username),
|
|
),
|
|
(user) => user.id,
|
|
);
|
|
}
|
|
return null;
|
|
};
|
|
|
|
renderChannelMembersList = () => {
|
|
const {
|
|
showMemberlist,
|
|
activeChannelId,
|
|
channelUsers,
|
|
memberFilterQuery,
|
|
} = this.state;
|
|
const filterRegx = new RegExp(memberFilterQuery, 'gi');
|
|
return (
|
|
<div
|
|
className={
|
|
showMemberlist ? 'mention__list mention__visible' : 'mention__list'
|
|
}
|
|
id="mentionList"
|
|
>
|
|
{showMemberlist
|
|
? Object.values(channelUsers[activeChannelId])
|
|
.filter((user) => user.username.match(filterRegx))
|
|
.map((user) => (
|
|
<div
|
|
key={user.username}
|
|
className="mention__user"
|
|
role="button"
|
|
onClick={this.addUserName}
|
|
tabIndex="0"
|
|
data-content={user.username}
|
|
onKeyUp={(e) => {
|
|
if (e.keyCode === 13) this.addUserName();
|
|
}}
|
|
>
|
|
<img
|
|
className="mention__user__image"
|
|
src={user.profile_image}
|
|
alt={user.name}
|
|
style={!user.profile_image ? { display: 'none' } : ' '}
|
|
/>
|
|
<span
|
|
style={{
|
|
padding: '3px 0px',
|
|
'font-size': '16px',
|
|
}}
|
|
>
|
|
{'@'}
|
|
{user.username}
|
|
<p>{user.name}</p>
|
|
</span>
|
|
</div>
|
|
))
|
|
: ' '}
|
|
</div>
|
|
);
|
|
};
|
|
|
|
handleEditMessageClose = () => {
|
|
this.setState({
|
|
startEditing: false,
|
|
markdownEdited: false,
|
|
activeEditMessage: { message: '', markdown: '' },
|
|
});
|
|
};
|
|
|
|
renderDeleteModal = () => {
|
|
const { showDeleteModal } = this.state;
|
|
return (
|
|
<div
|
|
id="message"
|
|
className={
|
|
showDeleteModal
|
|
? 'message__delete__modal crayons-modal crayons-modal--s absolute'
|
|
: 'message__delete__modal message__delete__modal__hide crayons-modal crayons-modal--s absolute'
|
|
}
|
|
aria-hidden={showDeleteModal}
|
|
aria-label="delete confirmation"
|
|
role="dialog"
|
|
>
|
|
<div className="crayons-modal__box">
|
|
<div className="crayons-modal__box__body">
|
|
<h3>Are you sure, you want to delete this message?</h3>
|
|
<div className="delete-actions__container">
|
|
<Button
|
|
className="crayons-btn crayons-btn--danger message__delete__button"
|
|
onClick={this.handleMessageDelete}
|
|
tabIndex="0"
|
|
onKeyUp={(e) => {
|
|
if (e.keyCode === 13) this.handleMessageDelete();
|
|
}}
|
|
>
|
|
Delete
|
|
</Button>
|
|
<Button
|
|
className="crayons-btn crayons-btn--secondary message__cancel__button"
|
|
onClick={this.handleCloseDeleteModal}
|
|
tabIndex="0"
|
|
onKeyUp={(e) => {
|
|
if (e.keyCode === 13) this.handleCloseDeleteModal();
|
|
}}
|
|
>
|
|
Cancel
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div className="crayons-modal__overlay" />
|
|
</div>
|
|
);
|
|
};
|
|
|
|
handleCloseDeleteModal = () => {
|
|
this.setState({ showDeleteModal: false, messageDeleteId: null });
|
|
};
|
|
|
|
handleMessageDelete = () => {
|
|
const { messageDeleteId } = this.state;
|
|
deleteMessage(messageDeleteId);
|
|
this.setState({ showDeleteModal: false });
|
|
};
|
|
|
|
handleJoiningRequest = (e) => {
|
|
sendChannelRequest(
|
|
e.target.dataset.channelId,
|
|
this.handleJoiningRequestSuccess,
|
|
this.handleFailure,
|
|
);
|
|
};
|
|
|
|
handleJoiningRequestSuccess = () => {
|
|
const { activeChannelId } = this.state;
|
|
this.setActiveContentState(activeChannelId, null);
|
|
this.setState({ fullscreenContent: null });
|
|
this.toggleSearchShowing();
|
|
};
|
|
|
|
renderChannelBackNav = () => {
|
|
return (
|
|
<Button
|
|
className="crayons-btn crayons-btn--icon-rounded crayons-btn--ghost active-channel__back-btn"
|
|
onClick={this.navigateToChannelsList}
|
|
onKeyUp={(e) => {
|
|
if (e.keyCode === 13) this.navigateToChannelsList(e);
|
|
}}
|
|
tabIndex="0"
|
|
>
|
|
<svg
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
viewBox="0 0 24 24"
|
|
width="24"
|
|
height="24"
|
|
className="crayons-icon"
|
|
>
|
|
<path d="M10.828 12l4.95 4.95-1.414 1.414L8 12l6.364-6.364 1.414 1.414z" />
|
|
</svg>
|
|
</Button>
|
|
);
|
|
};
|
|
|
|
renderChannelHeaderInner = () => {
|
|
const { activeChannel } = this.state;
|
|
if (activeChannel.channel_type === 'direct') {
|
|
return (
|
|
<a
|
|
href={`/${activeChannel.channel_username}`}
|
|
className="active-channel__title"
|
|
onClick={this.triggerActiveContent}
|
|
data-content="sidecar-user"
|
|
>
|
|
{activeChannel.channel_modified_slug}
|
|
</a>
|
|
);
|
|
}
|
|
return (
|
|
<a
|
|
href="#/"
|
|
onClick={this.triggerActiveContent}
|
|
data-content="chat_channel_setting"
|
|
>
|
|
{activeChannel.channel_name}
|
|
</a>
|
|
);
|
|
};
|
|
|
|
renderChannelConfigImage = () => {
|
|
const { activeContent, activeChannel, activeChannelId } = this.state;
|
|
if (
|
|
activeContent[activeChannelId] &&
|
|
activeContent[activeChannelId].type_of
|
|
) {
|
|
return '';
|
|
}
|
|
|
|
const path =
|
|
activeChannel.channel_type === 'direct'
|
|
? `/${activeChannel.channel_username}`
|
|
: `#`;
|
|
|
|
const dataContent =
|
|
activeChannel.channel_type === 'direct'
|
|
? 'sidecar-user'
|
|
: `chat_channel_setting`;
|
|
|
|
const contentLink =
|
|
activeChannel.channel_type === 'direct'
|
|
? `/${activeChannel.channel_username}`
|
|
: '#/';
|
|
|
|
return (
|
|
<a
|
|
className="crayons-btn crayons-btn--icon-rounded crayons-btn--ghost"
|
|
onClick={this.triggerActiveContent}
|
|
onKeyUp={(e) => {
|
|
if (e.keyCode === 13) this.triggerActiveContent(e);
|
|
}}
|
|
tabIndex="0"
|
|
href={contentLink}
|
|
data-content={dataContent}
|
|
>
|
|
<svg
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
viewBox="0 0 24 24"
|
|
width="24"
|
|
height="24"
|
|
className="crayons-icon"
|
|
data-content={dataContent}
|
|
>
|
|
<path
|
|
data-content={dataContent}
|
|
d="M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10-4.477 10-10 10zm0-2a8 8 0 1 0 0-16 8 8 0 0 0 0 16zM11 7h2v2h-2V7zm0 4h2v6h-2v-6z"
|
|
/>
|
|
</svg>
|
|
</a>
|
|
);
|
|
};
|
|
|
|
render() {
|
|
const { state } = this;
|
|
let channelHeader = <div className="active-channel__header"> </div>;
|
|
if (state.activeChannel) {
|
|
channelHeader = (
|
|
<div className="active-channel__header">
|
|
{this.renderChannelBackNav()}
|
|
{this.renderChannelHeaderInner()}
|
|
{this.renderChannelConfigImage()}
|
|
</div>
|
|
);
|
|
}
|
|
let fullscreenMode = '';
|
|
if (state.fullscreenContent === 'sidecar') {
|
|
fullscreenMode = 'chat--content-visible-full';
|
|
} else if (state.fullscreenContent === 'video') {
|
|
fullscreenMode = 'chat--video-visible-full';
|
|
}
|
|
return (
|
|
<div
|
|
data-testid="chat"
|
|
className={`chat chat--expanded
|
|
chat--${
|
|
state.videoPath ? 'video-visible' : 'video-not-visible'
|
|
} chat--${
|
|
state.activeContent[state.activeChannelId]
|
|
? 'content-visible'
|
|
: 'content-not-visible'
|
|
} ${fullscreenMode}`}
|
|
data-no-instant
|
|
aria-expanded={state.expanded}
|
|
>
|
|
{this.renderChatChannels()}
|
|
<div data-testid="active-chat" className="chat__activechat">
|
|
{this.renderActiveChatChannel(channelHeader)}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
}
|