docbrown/app/javascript/chat/channelDetails.jsx
Sarthak Sharma 75608e3e55 Feature🚀: Ability to mention anyone in open chats and notify them (#5460) [deploy]
* 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

* Feature 🚀 : Ability to mention member in Group [Frontend]

* Test Cases📝 : Test Cases added

* Feature 🚀 : Ability to mention member in Group [Backend]

* Bug Fix 🐞: Making messages realtime for open channels

* Feature 🚀 and Bug Fix 🐞: Ability to get notification if mentioned in open messages and increasing message number count

* Feature 🚀 : Ability to remove message count on channel open

* Bug Fix 🐞: Making Notification more reliable by checking if the new message is notified to user or not

* Feature 🚀 : Ability to metion @all and notify them

* Bug Fix 🐞: Minor fixes

* Fews tweaks in code

* Bug Fix 🐞: Feature was not present for Invite only Section

* Bug Fix 🐞: In open channels one can't mention all

* Bug Fix 🐞: Markdown conversion for all only in case of invite only

* Specs added

* Delete schema.rb

* Schema file updated

* Code refactoring

* Further Code refactoring backend

* Further Code refactoring backend

* Bug Fix 🐞: Mention List error problem in switching tabs

* Test Snapshots added

* Feature 🚀 : Ability to mention member in Group [Frontend]

* Test Cases📝 : Test Cases added

* Feature 🚀 : Ability to mention member in Group [Backend]

* Bug Fix 🐞: Making messages realtime for open channels

* Feature 🚀 and Bug Fix 🐞: Ability to get notification if mentioned in open messages and increasing message number count

* Feature 🚀 : Ability to remove message count on channel open

* Bug Fix 🐞: Making Notification more reliable by checking if the new message is notified to user or not

* Feature 🚀 : Ability to metion @all and notify them

* Bug Fix 🐞: Minor fixes

* Fews tweaks in code

* Bug Fix 🐞: Feature was not present for Invite only Section

* Bug Fix 🐞: In open channels one can't mention all

* Bug Fix 🐞: Markdown conversion for all only in case of invite only

* Specs added

* Code refactoring

* Further Code refactoring backend

* Further Code refactoring backend

* Bug Fix 🐞: Mention List error problem in switching tabs

* Test Snapshots added

* Feature 🚀: Ability to navigate through user list with keyboard only

* Bug Fix 🐞: Changing color of group avatars in dark themes

* Bug Fix 🐞: Setup pusher channel for open channels also
2020-01-23 18:06:35 -05:00

246 lines
6.9 KiB
JavaScript

import { h, Component } from 'preact';
import PropTypes from 'prop-types';
class ChannelDetails extends Component {
static propTypes = {
channel: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
};
constructor(props) {
super(props);
this.state = {
searchedUsers: [],
// invitations: [],
hasLeftChannel: false,
};
const algoliaId = document.querySelector("meta[name='algolia-public-id']")
.content;
const algoliaKey = document.querySelector("meta[name='algolia-public-key']")
.content;
const env = document.querySelector("meta[name='environment']").content;
const client = algoliasearch(algoliaId, algoliaKey); // eslint-disable-line no-undef
this.index = client.initIndex(`searchables_${env}`);
}
triggerUserSearch = e => {
const component = this;
const query = e.target.value;
const filters = {
hitsPerPage: 20,
attributesToRetrieve: ['id', 'title', 'path', 'user'],
attributesToHighlight: [],
filters: 'class_name:User',
};
if (query.length > 0) {
this.index.search(query, filters).then(content => {
component.setState({ searchedUsers: content.hits });
});
} else {
component.setState({ searchedUsers: [] });
}
};
triggerInvite = e => {
const component = this;
const id = e.target.dataset.content;
e.target.style.display = 'none';
fetch(`/chat_channel_memberships`, {
method: 'POST',
headers: {
Accept: 'application/json',
'X-CSRF-Token': window.csrfToken,
'Content-Type': 'application/json',
},
body: JSON.stringify({
chat_channel_membership: {
user_id: id,
chat_channel_id: component.props.channel.id,
},
}),
credentials: 'same-origin',
})
.then(response => response)
.then(this.handleInvitationSuccess)
.catch(null);
};
triggerLeaveChannel = e => {
e.preventDefault();
const id = e.target.dataset.content;
fetch(`/chat_channel_memberships/${id}`, {
method: 'DELETE',
headers: {
Accept: 'application/json',
'X-CSRF-Token': window.csrfToken,
'Content-Type': 'application/json',
},
body: JSON.stringify({}),
credentials: 'same-origin',
})
.then(response => response)
.then(this.handleLeaveChannelSuccess)
.catch(null);
};
handleLeaveChannelSuccess = () => {
this.setState({ hasLeftChannel: true });
};
handleInvitationSuccess = response => {
console.log(response); // eslint-disable-line no-console
};
userInList = (list, user) => {
const keys = Object.keys(list);
for (let i = 0; i < keys.length; i += 1) {
const key = keys[i];
if (user.id === list[key].id) {
return true;
}
}
return false;
};
render() {
const channel = this.props.channel; // eslint-disable-line
const users = Object.values(channel.channel_users).map(user => (
<div className="channeldetails__user">
<img
className="channeldetails__userprofileimage"
src={user.profile_image}
alt={`${user.username} profile`}
data-content="sidecar-user"
/>
<a
href={`/${user.username}`}
style={{ color: user.darker_color, padding: '3px 0px' }}
data-content="sidecar-user"
>
{user.name}
</a>
</div>
));
let subHeader = '';
if (users.length === 25) {
subHeader = <h3>Recently Active Members</h3>;
}
let modSection = '';
let searchedUsers = [];
let pendingInvites = [];
if (channel.channel_mod_ids.includes(window.currentUser.id)) {
// eslint-disable-next-line
searchedUsers = this.state.searchedUsers.map(user => {
if (!this.userInList(channel.pending_users_select_fields, user)) {
let invite = (
<button
type="button"
onClick={this.triggerInvite}
data-content={user.id}
>
Invite
</button>
);
if (this.userInList(channel.channel_users, user)) {
invite = (
<span className="channel__member">
is already in
{' '}
<em>{channel.channel_name}</em>
</span>
);
}
return (
<div className="channeldetails__searchedusers">
<a href={user.path} target="_blank" rel="noopener noreferrer">
<img src={user.user.profile_image_90} alt="profile_image" />
@
{user.user.username}
{' '}
-
{/* prettier-ignore */}
{' '}
{user.title}
</a>
{' '}
{invite}
</div>
);
}
});
pendingInvites = channel.pending_users_select_fields.map(user => (
<div className="channeldetails__pendingusers">
<a
href={`/${user.username}`}
target="_blank"
rel="noopener noreferrer"
data-content={`users/${user.id}`}
>
@
{user.username}
{' '}
-
{' '}
{user.name}
</a>
</div>
));
modSection = (
<div className="channeldetails__inviteusers">
<h2>Invite Members</h2>
<input onKeyUp={this.triggerUserSearch} placeholder="Find users" />
<div className="channeldetails__searchresults">{searchedUsers}</div>
<h2>Pending Invites:</h2>
{pendingInvites}
<div style={{ marginTop: '10px' }}>
Contact yo@dev.to for assistance.
</div>
</div>
); // eslint-disable-next-line
} else if (this.state.hasLeftChannel) {
modSection = (
<div className="channeldetails__leftchannel">
<h2>Danger Zone</h2>
<h3>
You have left this channel
{' '}
<span role="img" aria-label="emoji">
😢😢😢
</span>
</h3>
<h4>This may take a few minutes to be reflected in the sidebar</h4>
</div>
);
} else {
modSection = (
<div className="channeldetails__leavechannel">
<h2>Danger Zone</h2>
<button
type="button"
onClick={this.triggerLeaveChannel}
data-content={channel.id}
>
Leave Channel
</button>
</div>
);
}
return (
<div className="channeldetails">
<h1 className="channeldetails__name">{channel.channel_name}</h1>
<div
className="channeldetails__description"
style={{ marginBottom: '20px' }}
>
<em>{channel.description || ''}</em>
</div>
{subHeader}
{users}
{modSection}
</div>
);
}
}
export default ChannelDetails;