[deploy] 🚀 Feature: Chat Channel Request manager for mods (#7904)

* 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

* 🐞 Channel List bug in mobile view

* changes in joining request responses

* 🛠  Request Managers Frontend ready

* changes in routes for adding and removing remembership

* 🚀 New routes implemented for request manager

* fix issues

* changes in api for remove membership

* Merge conflict resolved

* 🐞 Channel List bug in mobile view

* changes in joining request responses

* 🛠  Request Managers Frontend ready

* changes in routes for adding and removing remembership

* 🚀 New routes implemented for request manager

* fix issues

* changes in api for remove membership

* 🛠Optimizing for CodeClimate

* 🛠Optimizing again for CodeClimate

* 🛠Optimizing again 2 for CodeClimate

* 🛠 Added more test cases

* 🛠 Optimizing code

* 🛠 vscode file changed

* 🛠  Updated schema as requested

Co-authored-by: Parasgr-code <paras.gaur@skynox.tech>
This commit is contained in:
Sarthak Sharma 2020-06-03 02:53:56 +05:30 committed by GitHub
parent dc48886b7d
commit 40dc8d1349
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 476 additions and 110 deletions

View file

@ -531,7 +531,8 @@
}
}
.chat__channelinvitationsindicator a {
.chat__channelinvitationsindicator a,
.chat__channelinvitationsindicator button {
background: linear-gradient(10deg, darken($green, 25%), darken($green, 15%));
text-align: center;
padding: 30px 0px;
@ -1265,3 +1266,11 @@
border-radius: '500px';
background-color: '#f5f6f7';
}
.request-card {
display: grid;
grid-template-columns: 200px 1fr;
}
.action button:nth-child(2n) {
margin-left: 10px;
}

View file

@ -60,6 +60,7 @@ class ChatChannelMembershipsController < ApplicationController
@chat_channel = ChatChannel.find(params[:chat_channel_id])
authorize @chat_channel, :update?
@chat_channel_membership = @chat_channel.chat_channel_memberships.find(params[:membership_id])
membership = ChatChannelMembership.find_by!(chat_channel_id: params[:chat_channel_id], user: current_user)
if params[:status] == "pending"
@chat_channel_membership.destroy
flash[:settings_notice] = "Invitation removed."
@ -67,8 +68,11 @@ class ChatChannelMembershipsController < ApplicationController
send_chat_action_message("@#{current_user.username} removed @#{@chat_channel_membership.user.username} from #{@chat_channel_membership.channel_name}", current_user, @chat_channel_membership.chat_channel_id, "removed_from_channel")
@chat_channel_membership.update(status: "removed_from_channel")
flash[:settings_notice] = "Removed #{@chat_channel_membership.user.name}"
respond_to do |format|
format.html { redirect_to edit_chat_channel_membership_path(membership) }
format.json { render json: { status: "success", message: "Membership removed" } }
end && return
end
membership = ChatChannelMembership.find_by!(chat_channel_id: params[:chat_channel_id], user: current_user)
redirect_to edit_chat_channel_membership_path(membership)
end
@ -120,7 +124,10 @@ class ChatChannelMembershipsController < ApplicationController
NotifyMailer.channel_invite_email(@chat_channel_membership, @chat_channel_membership.user).deliver_later
flash[:settings_notice] = "Accepted request of #{@chat_channel_membership.user.username} to join #{channel_name}."
membership = ChatChannelMembership.find_by!(chat_channel_id: @chat_channel_membership.chat_channel.id, user: current_user)
redirect_to(edit_chat_channel_membership_path(membership)) && return
respond_to do |format|
format.html { redirect_to(edit_chat_channel_membership_path(membership)) }
format.json { render json: { status: "success", message: "Accepted Request" } }
end && return
end
else
@chat_channel_membership.update(status: "rejected")

View file

@ -147,7 +147,7 @@ class ChatChannelsController < ApplicationController
def render_unopened_json_response
@chat_channels_memberships = if session_current_user_id
ChatChannelMembership.where(user_id: session_current_user_id).includes(:chat_channel).
ChatChannelMembership.where(user_id: session_current_user_id).includes(%i[chat_channel user]).
where(has_unopened_messages: true).
where(show_global_badge_notification: true).
where.not(status: %w[removed_from_channel left_channel]).
@ -177,9 +177,11 @@ class ChatChannelsController < ApplicationController
end
def render_joining_request_json_response
requested_memberships = current_user.chat_channel_memberships.includes(:chat_channel).
where(chat_channels: { discoverable: true }, role: "mod").pluck(:chat_channel_id).map { |membership_id| ChatChannel.find_by(id: membership_id).requested_memberships }
render json: { joining_requests: requested_memberships.flatten }
requested_memberships_id = current_user.chat_channel_memberships.includes(:chat_channel).
where(chat_channels: { discoverable: true }, role: "mod").pluck(:chat_channel_id).map { |membership_id| ChatChannel.find_by(id: membership_id).requested_memberships }.flatten.map(&:id)
@chat_channels_memberships = ChatChannelMembership.includes(%i[user chat_channel]).where(id: requested_memberships_id)
render "index.json"
end
def render_channels_html

View file

@ -1,10 +1,67 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`<Content /> as loading-user should render and test snapshot 1`] = `
exports[`<Content /> as channel-request should render and test snapshot 1`] = `
<div
class="activechatchannel__activecontent activechatchannel__activecontent--sidecar"
id="chat_activecontent"
>
<button
class="activechatchannel__activecontentexitbutton crayons-btn crayons-btn--secondary"
data-content="exit"
>
<svg
data-content="exit"
height="24"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M0 0h24v24H0z"
data-content="exit"
fill="none"
/>
<path
d="M12 10.586l4.95-4.95 1.414 1.414-4.95 4.95 4.95 4.95-1.414 1.414-4.95-4.95-4.95 4.95-1.414-1.414 4.95-4.95-4.95-4.95L7.05 5.636z"
data-content="exit"
/>
</svg>
</button>
<button
class="activechatchannel__activecontentexitbutton activechatchannel__activecontentexitbutton--fullscreen crayons-btn crayons-btn--secondary"
data-content="fullscreen"
style={
Object {
"left": "39px",
}
}
>
<svg
data-content="fullscreen"
height="24"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M0 0h24v24H0z"
data-content="fullscreen"
fill="none"
/>
<path
d="M20 3h2v6h-2V5h-4V3h4zM4 3h4v2H4v4H2V3h2zm16 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"
data-content="fullscreen"
/>
</svg>
</button>
</div>
`;
exports[`<Content /> as loading-user should render and test snapshot 1`] = `
<div
class="activechatchannel__activecontent activechatchannel__activecontent--sidecar"
id="chat_activecontent"
onClick={false}
>
<button
class="activechatchannel__activecontentexitbutton crayons-btn crayons-btn--secondary"
@ -56,8 +113,5 @@ exports[`<Content /> as loading-user should render and test snapshot 1`] = `
/>
</svg>
</button>
<div
class="loading-user"
/>
</div>
`;

View file

@ -0,0 +1,48 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`<RequestManager /> should render and test snapshot 1`] = `
<div
class="activechatchannel__activeArticle activesendrequest"
>
<div
class="request_manager_header crayons-card mb-6 grid grid-flow-row gap-6 p-6"
>
<h1>
Joining Request
</h1>
<h3>
Manage request comming to all the channels
</h3>
<div
class="crayons-field"
>
<h1>
ironman
</h1>
<div
class="request-card"
>
<p />
<div
class="action"
>
<button
class="crayons-btn crayons-btn--s crayons-btn--danger"
data-channel-id={2}
type="button"
>
Reject
</button>
<button
class="crayons-btn crayons-btn--s"
data-channel-id={2}
type="button"
>
Accept
</button>
</div>
</div>
</div>
</div>
</div>
`;

View file

@ -3,24 +3,45 @@ import render from 'preact-render-to-json';
import { shallow } from 'preact-render-spy';
import Content from '../content';
const getContent = () => (
<Content
onTriggerContent={false}
resource={{ type_of: 'loading-user' }}
activeChannelId={12345}
pusherKey="ASDFGHJKL"
githubToken=""
/>
);
const data = [
{
onTriggerContent: false,
resource: { type_of: 'channel-request' },
activeChannelId: 12345,
pusherKey: 'ASDFGHJKL',
githubToken: '',
},
{
onTriggerContent: false,
resource: { type_of: 'loading-user' },
activeChannelId: 1235,
pusherKey: 'ASDFGHJKL',
githubToken: '',
},
];
const getContent = (resource) => <Content resource={resource} />;
describe('<Content />', () => {
describe('as loading-user', () => {
it('should render and test snapshot', () => {
const tree = render(getContent());
const tree = render(getContent(data[0]));
expect(tree).toMatchSnapshot();
});
it('should have proper elements, attributes and content', () => {
const context = shallow(getContent());
const context = shallow(getContent(data[0]));
expect(
context.find('.activechatchannel__activecontent').exists(),
).toEqual(true);
});
});
describe('as channel-request', () => {
it('should render and test snapshot', () => {
const tree = render(getContent(data[1]));
expect(tree).toMatchSnapshot();
});
it('should have proper elements, attributes and content', () => {
const context = shallow(getContent(data[1]));
expect(
context.find('.activechatchannel__activecontent').exists(),
).toEqual(true);

View file

@ -0,0 +1,26 @@
import { h } from 'preact';
import render from 'preact-render-to-json';
import { shallow } from 'preact-render-spy';
import RequestManager from '../requestManager';
const data = [
{
id: 2,
channel_name: 'ironman',
},
];
const getRequestManager = (resource) => <RequestManager resource={resource} />;
describe('<RequestManager />', () => {
it('should render and test snapshot', () => {
const tree = render(getRequestManager(data));
expect(tree).toMatchSnapshot();
});
it('should have the proper elements, attributes and values', () => {
const context = shallow(getRequestManager(data));
expect(context.find('.request_manager_header').exists()).toEqual(true);
});
});

View file

@ -1,4 +1,4 @@
import { fetchSearch } from '../utilities/search';
import { createDataHash } from '../util';
export function getAllMessages(channelId, messageOffset, successCb, failureCb) {
fetch(`/chat_channels/${channelId}?message_offset=${messageOffset}`, {
@ -98,37 +98,22 @@ export function conductModeration(
}
export function getChannels(
query,
retrievalID,
searchType,
paginationNumber,
searchParams,
additionalFilters,
successCb,
_failureCb,
) {
const dataHash = {};
if (additionalFilters.filters) {
const [key, value] = additionalFilters.filters.split(':');
dataHash[key] = value;
}
dataHash.per_page = 30;
dataHash.page = paginationNumber;
dataHash.channel_text = query;
if (searchType === 'discoverable') {
dataHash.user_id = 'all';
}
const responsePromise = fetchSearch('chat_channels', dataHash);
return responsePromise.then((response) => {
const channels = response.result;
return createDataHash(additionalFilters, searchParams).then((response) => {
if (
retrievalID === null ||
channels.filter((e) => e.chat_channel_id === retrievalID).length === 1
searchParams.retrievalID === null ||
response.result.filter(
(e) => e.chat_channel_id === searchParams.retrievalID,
).length === 1
) {
successCb(channels, query);
successCb(response.result, searchParams.query);
} else {
fetch(
`/chat_channel_memberships/find_by_chat_channel_id?chat_channel_id=${retrievalID}`,
`/chat_channel_memberships/find_by_chat_channel_id?chat_channel_id=${searchParams.retrievalID}`,
{
Accept: 'application/json',
'Content-Type': 'application/json',
@ -137,8 +122,8 @@ export function getChannels(
)
.then((individualResponse) => individualResponse.json())
.then((json) => {
channels.unshift(json);
successCb(channels, query);
response.result.unshift(json);
successCb(response.result, searchParams.query);
});
}
});
@ -187,6 +172,17 @@ export function getChannelInvites(successCb, failureCb) {
.catch(failureCb);
}
export function getJoiningRequest(successCb, failureCb) {
fetch('/chat_channels?state=joining_request', {
Accept: 'application/json',
'Content-Type': 'application/json',
credentials: 'same-origin',
})
.then((response) => response.json())
.then(successCb)
.catch(failureCb);
}
export function sendChannelInviteAction(id, action, successCb, failureCb) {
fetch(`/chat_channel_memberships/${id}`, {
method: 'PUT',
@ -226,23 +222,3 @@ export function deleteMessage(messageId, successCb, failureCb) {
.then(successCb)
.catch(failureCb);
}
export function sendChannelRequest(id, successCb, failureCb) {
fetch(`/join_chat_channel`, {
method: 'POST',
headers: {
Accept: 'application/json',
'X-CSRF-Token': window.csrfToken,
'Content-Type': 'application/json',
},
body: JSON.stringify({
chat_channel_membership: {
chat_channel_id: id,
},
}),
credentials: 'same-origin',
})
.then((response) => response.json())
.then(successCb)
.catch(failureCb);
}

View file

@ -0,0 +1,71 @@
export function rejectJoiningRequest(
channelId,
membershipId,
successCb,
failureCb,
) {
fetch(`/chat_channel_memberships/remove_membership`, {
method: 'POST',
headers: {
Accept: 'application/json',
'X-CSRF-Token': window.csrfToken,
'Content-Type': 'application/json',
},
body: JSON.stringify({
status: 'pending',
chat_channel_id: channelId,
membership_id: membershipId,
}),
credentials: 'same-origin',
})
.then((response) => response.json())
.then(successCb)
.catch(failureCb);
}
export function acceptJoiningRequest(
channelId,
membershipId,
successCb,
failureCb,
) {
fetch(`/chat_channel_memberships/add_membership`, {
method: 'POST',
headers: {
Accept: 'application/json',
'X-CSRF-Token': window.csrfToken,
'Content-Type': 'application/json',
},
body: JSON.stringify({
chat_channel_id: channelId,
membership_id: membershipId,
chat_channel_membership: {
user_action: 'accept',
},
}),
credentials: 'same-origin',
})
.then((response) => response.json())
.then(successCb)
.catch(failureCb);
}
export function sendChannelRequest(id, successCb, failureCb) {
fetch(`/join_chat_channel`, {
method: 'POST',
headers: {
Accept: 'application/json',
'X-CSRF-Token': window.csrfToken,
'Content-Type': 'application/json',
},
body: JSON.stringify({
chat_channel_membership: {
chat_channel_id: id,
},
}),
credentials: 'same-origin',
})
.then((response) => response.json())
.then(successCb)
.catch(failureCb);
}

View file

@ -11,7 +11,7 @@ const Channels = ({
unopenedChannelIds,
handleSwitchChannel,
expanded,
filterQuery,
filterQuery = '',
channelsLoaded,
currentUserId,
triggerActiveContent,
@ -32,7 +32,6 @@ const Channels = ({
);
},
);
const channels = sortedChatChannels.activeChannels.map((channel) => {
const isActive = parseInt(activeChannelId, 10) === channel.chat_channel_id;
const isUnopened =

View file

@ -11,11 +11,16 @@ import {
getUnopenedChannelIds,
getContent,
getChannelInvites,
getJoiningRequest,
sendChannelInviteAction,
deleteMessage,
editMessage,
} from './actions/actions';
import {
sendChannelRequest,
} from './actions';
rejectJoiningRequest,
acceptJoiningRequest,
} from './actions/requestActions';
import {
hideMessages,
scrollToBottom,
@ -46,7 +51,6 @@ export default class Chat extends Component {
super(props);
const chatChannels = JSON.parse(props.chatChannels);
const chatOptions = JSON.parse(props.chatOptions);
this.debouncedChannelFilter = debounceAction(
this.triggerChannelFilter.bind(this),
);
@ -75,6 +79,7 @@ export default class Chat extends Component {
isMobileDevice: typeof window.orientation !== 'undefined',
subscribedPusherChannels: [],
inviteChannels: [],
joiningRequests: [],
messageOffset: 0,
showDeleteModal: false,
messageDeleteId: null,
@ -128,14 +133,13 @@ export default class Chat extends Component {
channelTypeFilter === 'all'
? {}
: { filters: `channel_type:${channelTypeFilter}` };
getChannels(
'',
activeChannelId,
'',
channelPaginationNum,
filters,
this.loadChannels,
);
const searchParams = {
query: '',
retrievalID: activeChannelId,
searchType: '',
paginationNumber: channelPaginationNum,
};
getChannels(searchParams, filters, this.loadChannels);
getUnopenedChannelIds(this.markUnopenedChannelIds);
}
if (!isMobileDevice) {
@ -147,6 +151,7 @@ export default class Chat extends Component {
.addEventListener('scroll', this.handleChannelScroll);
}
getChannelInvites(this.handleChannelInvites, null);
getJoiningRequest(this.handleChannelJoiningRequest, null);
}
shouldComponentUpdate(nextProps, nextState) {
@ -521,14 +526,13 @@ export default class Chat extends Component {
channelTypeFilter === 'all'
? {}
: { filters: `channel_type:${channelTypeFilter}` };
getChannels(
filterQuery,
activeChannelId,
'',
channelPaginationNum,
filters,
this.loadPaginatedChannels,
);
const searchParams = {
query: filterQuery,
retrievalID: activeChannelId,
searchType: '',
paginationNumber: channelPaginationNum,
};
getChannels(searchParams, filters, this.loadPaginatedChannels);
}
};
@ -536,6 +540,10 @@ export default class Chat extends Component {
this.setState({ inviteChannels: response });
};
handleChannelJoiningRequest = (res) => {
this.setState({ joiningRequests: res });
};
handleKeyDown = (e) => {
const { showMemberlist, activeContent, activeChannelId } = this.state;
const enterPressed = e.keyCode === 13;
@ -823,6 +831,24 @@ export default class Chat extends Component {
}
};
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,
);
};
triggerActiveContent = (e) => {
if (
// Trying to open in new tab
@ -859,6 +885,13 @@ export default class Chat extends Component {
handleJoiningRequest: this.handleJoiningRequest,
type_of: 'channel-request',
});
} else if (content === 'sidecar-joining-request-manager') {
this.setActiveContent({
data: this.state.joiningRequests,
type_of: 'channel-request-manager',
handleRequestRejection: this.handleRequestRejection,
handleRequestApproval: this.handleRequestApproval,
});
} else if (content === 'sidecar_all') {
this.setActiveContentState(activeChannelId, {
type_of: 'loading-post',
@ -970,17 +1003,17 @@ export default class Chat extends Component {
fetchingPaginatedChannels: false,
});
const filters = type === 'all' ? {} : { filters: `channel_type:${type}` };
const searchParams = {
query: filterQuery,
retrievalID: null,
searchType: '',
paginationNumber: 0,
};
if (filterQuery && type !== 'direct') {
getChannels(
filterQuery,
null,
'discoverable',
0,
filters,
this.loadChannels,
);
searchParams.searchType = 'discoverable';
getChannels(searchParams, filters, this.loadChannels);
} else {
getChannels(filterQuery, null, '', 0, filters, this.loadChannels);
getChannels(searchParams, filters, this.loadChannels);
}
};
@ -1083,17 +1116,17 @@ export default class Chat extends Component {
channelTypeFilter === 'all'
? {}
: { filters: `channel_type:${channelTypeFilter}` };
const searchParams = {
query: e.target.value,
retrievalID: null,
searchType: '',
paginationNumber: 0,
};
if (e.target.value) {
getChannels(
e.target.value,
null,
'discoverable',
0,
filters,
this.loadChannels,
);
searchParams.searchType = 'discoverable';
getChannels(searchParams, filters, this.loadChannels);
} else {
getChannels(e.target.value, null, '', 0, filters, this.loadChannels);
getChannels(searchParams, filters, this.loadChannels);
}
};
@ -1120,7 +1153,13 @@ export default class Chat extends Component {
document.getElementById('chatchannelsearchbar').focus();
}, 100);
} else {
getChannels('', null, '', 0, '', this.loadChannels);
const searchParams = {
query: '',
retrievalID: null,
searchType: '',
paginationNumber: 0,
};
getChannels(searchParams, this.loadChannels);
this.setState({ filterQuery: '' });
}
this.setState({ searchShowing: !this.state.searchShowing });
@ -1133,6 +1172,7 @@ export default class Chat extends Component {
const notificationsButton = '';
let notificationsState = '';
let invitesButton = '';
let joiningRequestButton = '';
if (notificationsPermission === 'granted') {
notificationsState = (
<div className="chat_chatconfig chat_chatconfig--on">
@ -1164,6 +1204,23 @@ export default class Chat extends Component {
</div>
);
}
if (state.joiningRequests.length > 0) {
joiningRequestButton = (
<div className="chat__channelinvitationsindicator">
<button
onClick={this.triggerActiveContent}
data-content="sidecar-joining-request-manager"
type="button"
>
<span role="img" aria-label="emoji">
👋
</span>
{' '}
New Requests
</button>
</div>
);
}
if (state.expanded) {
return (
<div className="chat__channels chat__channels--expanded">
@ -1186,6 +1243,7 @@ export default class Chat extends Component {
''
)}
{invitesButton}
{joiningRequestButton}
<div className="chat__channeltypefilter">
<button
className="chat__channelssearchtoggle"
@ -1641,6 +1699,17 @@ export default class Chat extends Component {
);
};
handleJoiningManagerSuccess = (membershipId) => {
const { activeChannelId } = this.state;
this.setState({
joiningRequests: this.state.joiningRequests.filter(
(req) => req.membership_id !== parseInt(membershipId, 10),
),
});
this.setActiveContentState(activeChannelId, null);
this.setState({ fullscreenContent: null });
};
handleJoiningRequestSuccess = () => {
const { activeChannelId } = this.state;
this.setActiveContentState(activeChannelId, null);

View file

@ -2,6 +2,8 @@ import { h, Component } from 'preact';
import PropTypes from 'prop-types';
import Article from './article';
import ChannelRequest from './channelRequest';
import RequestManager from './requestManager';
export default class Content extends Component {
static propTypes = {
resource: PropTypes.object,
@ -81,4 +83,13 @@ function display(props) {
/>
);
}
if (resource.type_of === 'channel-request-manager') {
return (
<RequestManager
resource={resource.data}
handleRequestRejection={resource.handleRequestRejection}
handleRequestApproval={resource.handleRequestApproval}
/>
);
}
}

View file

@ -0,0 +1,56 @@
import { h } from 'preact';
import PropTypes from 'prop-types';
const RequestManager = ({
resource: data,
handleRequestRejection,
handleRequestApproval,
}) => {
return (
<div className="activechatchannel__activeArticle activesendrequest">
<div className="request_manager_header crayons-card mb-6 grid grid-flow-row gap-6 p-6">
<h1>Joining Request</h1>
<h3>Manage request comming to all the channels</h3>
{data.map((request) => (
<div className="crayons-field">
<h1>{request.channel_name}</h1>
<div className="request-card">
<p>{request.member_name}</p>
<div className="action">
<button
type="button"
className="crayons-btn crayons-btn--s crayons-btn--danger"
onClick={handleRequestRejection}
data-channel-id={request.id}
data-membership-id={request.membership_id}
>
{' '}
Reject
</button>
<button
type="button"
className="crayons-btn crayons-btn--s"
onClick={handleRequestApproval}
data-channel-id={request.id}
data-membership-id={request.membership_id}
>
{' '}
Accept
</button>
</div>
</div>
</div>
))}
</div>
</div>
);
};
RequestManager.propTypes = {
resource: PropTypes.shape({
data: PropTypes.object,
}).isRequired,
handleRequestRejection: PropTypes.func.isRequired,
handleRequestApproval: PropTypes.func.isRequired,
};
export default RequestManager;

View file

@ -1,3 +1,5 @@
import { fetchSearch } from '../utilities/search';
import 'intersection-observer';
export function getCsrfToken() {
@ -107,3 +109,18 @@ export const channelSorter = (channels, currentUserId, filterQuery) => {
.filter((channel) => !ChannelIds[0].includes(channel.chat_channel_id));
return { activeChannels, discoverableChannels };
};
export const createDataHash = (additionalFilters, searchParams) => {
const dataHash = {};
if (additionalFilters.filters) {
const [key, value] = additionalFilters.filters.split(':');
dataHash[key] = value;
}
dataHash.per_page = 30;
dataHash.page = searchParams.paginationNumber;
dataHash.channel_text = searchParams.query;
if (searchParams.searchType === 'discoverable') {
dataHash.user_id = 'all';
}
return fetchSearch('chat_channels', dataHash);
};

View file

@ -16,4 +16,5 @@ json.array!(memberships) do |membership|
json.adjusted_slug membership.chat_channel.adjusted_slug(current_user)
json.membership_id membership.id
json.member_name membership.user.username
end

View file

@ -57,8 +57,7 @@ RSpec.describe "ChatChannels", type: :request do
membership.chat_channel.update(discoverable: true)
sign_in user
get "/chat_channels?state=joining_request"
expect(response.body).to include("\"status\":\"joining_request\"")
expect(response.body).to include("joining_requests")
expect(response.body).to include("\"member_name\":\"#{membership.user.username}\"")
end
end