diff --git a/app/controllers/chat_channel_memberships_controller.rb b/app/controllers/chat_channel_memberships_controller.rb
index 236a89420..3ce538832 100644
--- a/app/controllers/chat_channel_memberships_controller.rb
+++ b/app/controllers/chat_channel_memberships_controller.rb
@@ -28,6 +28,16 @@ class ChatChannelMembershipsController < ApplicationController
render "chat_channels/index.json"
end
+ def destroy
+ @chat_channel_membership = ChatChannel.find(params[:id]).
+ chat_channel_memberships.where(user_id: current_user.id).first
+ authorize @chat_channel_membership
+ @chat_channel_membership.update(status: "left_channel")
+ @chat_channel_membership.chat_channel.index!
+ @chat_channels_memberships = []
+ render json: { result: "left channel" }, status: 201
+ end
+
def permitted_params
params.require(:chat_channel_membership).permit(:user_id, :chat_channel_id, :user_action, :id)
end
diff --git a/app/javascript/chat/channelDetails.jsx b/app/javascript/chat/channelDetails.jsx
index 51fe6a7d4..1c424fca4 100644
--- a/app/javascript/chat/channelDetails.jsx
+++ b/app/javascript/chat/channelDetails.jsx
@@ -1,20 +1,21 @@
-
-
import { h, Component } from 'preact';
-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 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);
-const index = client.initIndex('searchables_'+env);
+const index = client.initIndex(`searchables_${ env}`);
export default class ChannelDetails extends Component {
constructor(props) {
super(props);
this.state = {
searchedUsers: [],
- invitations: []
- }
+ invitations: [],
+ hasLeftChannel: false,
+ };
}
triggerUserSearch = e => {
@@ -22,28 +23,23 @@ export default class ChannelDetails extends Component {
const query = e.target.value;
const filters = {
hitsPerPage: 20,
- attributesToRetrieve: [
- 'id',
- 'title',
- 'path'
- ],
+ attributesToRetrieve: ['id', 'title', 'path'],
attributesToHighlight: [],
filters: 'class_name:User',
- }
+ };
if (query.length > 0) {
- index.search(query, filters)
- .then(function(content) {
- component.setState({searchedUsers:content.hits})
+ index.search(query, filters).then((content) => {
+ component.setState({ searchedUsers: content.hits });
});
} else {
- component.setState({searchedUsers:[]})
+ component.setState({ searchedUsers: [] });
}
- }
+ };
triggerInvite = e => {
const component = this;
const id = e.target.dataset.content;
- e.target.style.display = 'none'
+ e.target.style.display = 'none';
fetch(`/chat_channel_memberships`, {
method: 'POST',
headers: {
@@ -55,64 +51,131 @@ export default class ChannelDetails extends Component {
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 = response => {
+ this.setState({ hasLeftChannel: true });
+ };
handleInvitationSuccess = response => {
- console.log(response)
- }
+ console.log(response);
+ };
render() {
- const channel = this.props.channel
- const users = Object.values(channel.channel_users).map((user) => {
- return
+ const channel = this.props.channel;
+ const users = Object.values(channel.channel_users).map(user => (
+
- });
- let subHeader = ''
+ ));
+ let subHeader = '';
if (users.length === 80) {
- subHeader =
Recently Active Members
+ subHeader =
Recently Active Members
;
}
- let modSection = ''
+ let modSection = '';
let searchedUsers = [];
let pendingInvites = [];
if (channel.channel_mod_ids.includes(window.currentUser.id)) {
- searchedUsers = this.state.searchedUsers.map((user) => {
- return
- })
- pendingInvites = channel.pending_users_select_fields.map((user) => {
- return
- })
- modSection =
-
Invite Members
-
- {searchedUsers}
-
Pending Invites:
- {pendingInvites}
-
All functionality is early beta. Contact us if you need help with anything.
-
- }
- return
-
{channel.channel_name}
- {subHeader}
-
- {channel.description || ''}
-
- {users}
- {modSection}
-
+ searchedUsers = this.state.searchedUsers.map(user => (
+
+ ));
+ pendingInvites = channel.pending_users_select_fields.map(user => (
+
+ ));
+ modSection = (
+
+
Invite Members
+
+ {searchedUsers}
+
Pending Invites:
+ {pendingInvites}
+
+ All functionality is early beta. Contact us if you need help with
+ anything.
+
+
+ );
+ } else if (this.state.hasLeftChannel) {
+ modSection = (
+
+
Danger Zone
+
You have left this channel 😢😢😢
+
It may not be immediately in the sidebar
+
+ Contact the admins at yo@dev.to if
+ this was a mistake
+
+
+ );
+ } else {
+ modSection = (
+
+
Danger Zone
+
+
+ );
+ }
+ return (
+
+
{channel.channel_name}
+ {subHeader}
+
+ {channel.description || ''}
+
+ {users}
+ {modSection}
+
+ );
}
-
-}
\ No newline at end of file
+}
diff --git a/app/javascript/packs/chat.jsx b/app/javascript/packs/chat.jsx
index c6625c2cf..0d265f26b 100644
--- a/app/javascript/packs/chat.jsx
+++ b/app/javascript/packs/chat.jsx
@@ -10,18 +10,29 @@ HTMLDocument.prototype.ready = new Promise(resolve => {
return null;
});
-document.ready.then(
+loadChat(); // Load initially
+window.InstantClick.on('change', () => {
+ loadChat(); // Load via instantclick nav
+});
+
+function loadChat() {
getUserDataAndCsrfToken().then(currentUser => {
if (document.getElementById('chat')) {
- const { chatChannels, pusherKey, chatOptions, algoliaKey, algoliaId, algoliaIndex, githubToken } = document.getElementById(
- 'chat',
- ).dataset;
+ const {
+ chatChannels,
+ pusherKey,
+ chatOptions,
+ algoliaKey,
+ algoliaId,
+ algoliaIndex,
+ githubToken,
+ } = document.getElementById('chat').dataset;
window.currentUser = currentUser;
window.csrfToken = document.querySelector(
"meta[name='csrf-token']",
).content;
const renderTarget = document.getElementById('chat');
- const placeholder = document.getElementById('chat_placeholder')
+ const placeholder = document.getElementById('chat_placeholder');
const root = render(
,
- renderTarget, renderTarget.firstChild
+ renderTarget,
+ renderTarget.firstChild,
);
if (placeholder) {
renderTarget.removeChild(placeholder);
}
window.InstantClick.on('change', () => {
- render('', document.body, root);
+ if (window.location.href.indexOf('/connect') != -1) {
+ const root = render(
+
,
+ renderTarget,
+ renderTarget.firstChild,
+ );
+ }
});
}
- }),
-);
+ });
+}
diff --git a/app/policies/chat_channel_membership_policy.rb b/app/policies/chat_channel_membership_policy.rb
index 9f93edaf8..bd6a32bc6 100644
--- a/app/policies/chat_channel_membership_policy.rb
+++ b/app/policies/chat_channel_membership_policy.rb
@@ -1,6 +1,9 @@
class ChatChannelMembershipPolicy < ApplicationPolicy
-
def update?
record.present? && user.id == record.user_id
end
-end
\ No newline at end of file
+
+ def destroy?
+ record.present? && user.id == record.user_id
+ end
+end
diff --git a/config/routes.rb b/config/routes.rb
index 519c456ac..9f30ede31 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -73,7 +73,7 @@ Rails.application.routes.draw do
resources :messages, only: [:create]
resources :chat_channels, only: [:index, :show, :create, :update]
- resources :chat_channel_memberships, only: [ :create, :update]
+ resources :chat_channel_memberships, only: [ :create, :update, :destroy]
resources :articles, only: [:update,:create,:destroy]
resources :comments, only: [:create,:update,:destroy]
resources :users, only: [:update]
diff --git a/spec/policies/chat_channel_membership_policy_spec.rb b/spec/policies/chat_channel_membership_policy_spec.rb
index f8d8980ea..afa75a17b 100644
--- a/spec/policies/chat_channel_membership_policy_spec.rb
+++ b/spec/policies/chat_channel_membership_policy_spec.rb
@@ -15,13 +15,13 @@ RSpec.describe ChatChannelMembershipPolicy do
let(:user) { create(:user) }
let(:chat_channel) { create(:chat_channel ) }
let(:chat_channel_membership) { create(:chat_channel_membership, user_id: user.id, chat_channel_id: chat_channel.id ) }
- it { is_expected.to permit_actions(%i[update]) }
+ it { is_expected.to permit_actions(%i[update destroy]) }
end
context "when user does not belong to membership" do
let(:user) { create(:user) }
let(:other_user) { create(:user) }
let(:chat_channel) { create(:chat_channel ) }
let(:chat_channel_membership) { create(:chat_channel_membership, user_id: other_user.id, chat_channel_id: chat_channel.id ) }
- it { is_expected.to forbid_actions(%i[update]) }
+ it { is_expected.to forbid_actions(%i[update destroy]) }
end
end
\ No newline at end of file
diff --git a/spec/requests/chat_channel_memberships_spec.rb b/spec/requests/chat_channel_memberships_spec.rb
index acf6841c1..254d5dc04 100644
--- a/spec/requests/chat_channel_memberships_spec.rb
+++ b/spec/requests/chat_channel_memberships_spec.rb
@@ -61,4 +61,18 @@ RSpec.describe "ChatChannelMemberships", type: :request do
end.to raise_error(Pundit::NotAuthorizedError)
end
end
-end
\ No newline at end of file
+
+ describe "DELETE /chat_channel_memberships/:id" do
+ before do
+ user.add_role(:super_admin)
+ post "/chat_channel_memberships", params: {
+ chat_channel_membership: {user_id: second_user.id, chat_channel_id: chat_channel.id}}
+ end
+ it "leaves chat channel" do
+ membership = ChatChannelMembership.last
+ sign_in second_user
+ delete "/chat_channel_memberships/#{membership.chat_channel.id}", params: {}
+ expect(ChatChannelMembership.find(membership.id).status).to eq("left_channel")
+ end
+ end
+end