[deploy] 🚀 Feature: Ability to search discoverable channels and send request to join (#7385)
* 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 * added new column discoverable for public channel and changes in elasticsearch * fix corrections * changes in serializer file, added channel_discoverable * added checkbox with discoverable policy * changes in code for discoverable channels * accept and reject member invitation by mod * add joining request to closed groups * fixed merge error * changes in joining member to closed groups * join to closed group * changes in message action of joining * changes in chat upopened json * 🚀Feature : Ability to search Global Channels * touch updated_at in after commit update * 🚀Feature : Ability to send request to discoverable channel * 🚀Feature : Ability to send request to discoverable channel * created new route for joining closed channel * 🚀 Success handle on joining request sent * removed redundant code * join_channel improvement, removed authorization for join channel * list of joining requests * added joining_request status channels on search list * changes in mailer and query builder optimized * 🛠 Adding filter for new Query * added rspec for add membership method in controller * rspec for sending join channel request * invite join request channel list rspec * test case for query builder discoverable * viewable and discoverable channel list * refactored logic for search channels * 🚀 Check if Request already sent * 🛠 Optimizing code and making channelButton Component * 🛠 Optimizing codefor SVG problem * 🛠 Optimized action.js * changes in search controller query * hot fix * removed unwanted code * 🛠 Fix the Channel Name problem * 🛠 Optimizing code further for CodeClimate * added new column discoverable for public channel and changes in elasticsearch * fix corrections * changes in serializer file, added channel_discoverable * added checkbox with discoverable policy * changes in code for discoverable channels * accept and reject member invitation by mod * add joining request to closed groups * fixed merge error * changes in joining member to closed groups * join to closed group * changes in message action of joining * changes in chat upopened json * touch updated_at in after commit update * 🚀Feature : Ability to search Global Channels * 🚀Feature : Ability to send request to discoverable channel * 🚀Feature : Ability to send request to discoverable channel * created new route for joining closed channel * 🚀 Success handle on joining request sent * removed redundant code * join_channel improvement, removed authorization for join channel * list of joining requests * added joining_request status channels on search list * changes in mailer and query builder optimized * added rspec for add membership method in controller * rspec for sending join channel request * invite join request channel list rspec * 🛠 Adding filter for new Query * test case for query builder discoverable * viewable and discoverable channel list * refactored logic for search channels * 🚀 Check if Request already sent * 🛠 Optimizing code and making channelButton Component * 🛠 Optimizing codefor SVG problem * 🛠 Optimized action.js * changes in search controller query * hot fix * 🛠 Fix the Channel Name problem * 🛠 Fixing merge problem * 🛠 Optimizing code further for CodeClimate * updated UI for membership edit * fixed test casses and fixed UI for chat_channel_edit * 🛠napshots added * test cases fixed * 🛠 Test cases added for new component * channel settings accesible only by mod * 🛠 More Test cases added * 🛠 Svg code optimized * 🛠 Svg code optimized in videocontent * optimized code for search query * fix test case for query builder * changes in joining closed channel logic * refactored join channel * redirect to edit channel after joining request * changes in test case for redirect * optimized code * 🛠 Eslint bugs fixed * test case fixed * optimization * olving channel repetition problem * optimization of code for query builder * changes in query builder * test cases fixed * 🛠 Handling reduntant data on frontend * 🛠 Don't show data if no filter query * 🛠 Optimized code for fixing bugs and code coverage * 🛠 Optimizing code further for CodeClimate Co-authored-by: Parasgr-code <paras.gaur@skynox.tech>
This commit is contained in:
parent
d89f4e14d7
commit
90d60bf73f
36 changed files with 997 additions and 348 deletions
|
|
@ -1214,3 +1214,54 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
.joining-message {
|
||||
display: grid;
|
||||
margin-top: 30px;
|
||||
justify-content: center;
|
||||
h3,
|
||||
h2 {
|
||||
width: 90%;
|
||||
margin-top: 10px;
|
||||
text-align: center;
|
||||
justify-self: center;
|
||||
}
|
||||
}
|
||||
.user-picture,
|
||||
.send-request {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin-top: 10vh;
|
||||
}
|
||||
.user-picture {
|
||||
.chatmessage__profilepic {
|
||||
.chatmessagebody__profileimage {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
border: 1px solid var(--base-30);
|
||||
}
|
||||
}
|
||||
}
|
||||
.send-request {
|
||||
.cta {
|
||||
margin: 4px 6px 4px 0;
|
||||
border-radius: 3px;
|
||||
font-weight: 600px;
|
||||
font-family: 'HelveticaNeue-CondensedBold', 'HelveticaNeueBoldCondensed',
|
||||
'HelveticaNeue-Bold-Condensed', 'Helvetica Neue Bold Condensed',
|
||||
'HelveticaNeueBold', 'HelveticaNeue-Bold', 'Helvetica Neue Bold',
|
||||
'HelveticaNeue', 'Helvetica Neue', 'TeXGyreHerosCnBold', 'Helvetica',
|
||||
'Tahoma', 'Geneva', 'Arial Narrow', 'Arial', sans-serif;
|
||||
font-size: 18px;
|
||||
border: 0px;
|
||||
height: 50px;
|
||||
padding: 0 30px;
|
||||
}
|
||||
}
|
||||
.loading-user {
|
||||
height: '210px';
|
||||
width: '210px';
|
||||
margin: ' 15px auto';
|
||||
display: 'block';
|
||||
border-radius: '500px';
|
||||
background-color: '#f5f6f7';
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
class ChatChannelMembershipsController < ApplicationController
|
||||
after_action :verify_authorized
|
||||
after_action :verify_authorized, except: :join_channel
|
||||
include MessagesHelper
|
||||
|
||||
def index
|
||||
|
|
@ -39,6 +39,23 @@ class ChatChannelMembershipsController < ApplicationController
|
|||
redirect_to edit_chat_channel_membership_path(membership)
|
||||
end
|
||||
|
||||
def join_channel
|
||||
membership_params = params[:chat_channel_membership]
|
||||
chat_channel = ChatChannel.find(membership_params[:chat_channel_id])
|
||||
existing_membership = ChatChannelMembership.find_by(user_id: current_user.id, chat_channel_id: chat_channel.id)
|
||||
if existing_membership.present? && %w[active joining_request].exclude?(existing_membership.status)
|
||||
status = existing_membership.update(status: "joining_request", role: "member")
|
||||
else
|
||||
membership = ChatChannelMembership.new(user_id: current_user.id, chat_channel_id: chat_channel.id, role: "member", status: "joining_request")
|
||||
status = membership.save
|
||||
end
|
||||
if status
|
||||
render json: { status: "success", message: "Request Sent" }
|
||||
else
|
||||
render json: { status: 400, message: "Unable to join channel" }, status: :bad_request
|
||||
end
|
||||
end
|
||||
|
||||
def remove_membership
|
||||
@chat_channel = ChatChannel.find(params[:chat_channel_id])
|
||||
authorize @chat_channel, :update?
|
||||
|
|
@ -55,11 +72,18 @@ class ChatChannelMembershipsController < ApplicationController
|
|||
redirect_to edit_chat_channel_membership_path(membership)
|
||||
end
|
||||
|
||||
def add_membership
|
||||
@chat_channel = ChatChannel.find(params[:chat_channel_id])
|
||||
authorize @chat_channel, :update?
|
||||
@chat_channel_membership = @chat_channel.chat_channel_memberships.find(params[:membership_id])
|
||||
respond_to_invitation(@chat_channel_membership.status) if permitted_params[:user_action].present? && @chat_channel_membership.status == "joining_request"
|
||||
end
|
||||
|
||||
def update
|
||||
@chat_channel_membership = ChatChannelMembership.find(params[:id])
|
||||
authorize @chat_channel_membership
|
||||
if permitted_params[:user_action].present?
|
||||
respond_to_invitation
|
||||
respond_to_invitation(@chat_channel_membership.status)
|
||||
else
|
||||
@chat_channel_membership.update(permitted_params)
|
||||
flash[:settings_notice] = "Personal settings updated."
|
||||
|
|
@ -84,12 +108,20 @@ class ChatChannelMembershipsController < ApplicationController
|
|||
params.require(:chat_channel_membership).permit(:user_action, :show_global_badge_notification)
|
||||
end
|
||||
|
||||
def respond_to_invitation
|
||||
def respond_to_invitation(previous_status)
|
||||
if permitted_params[:user_action] == "accept"
|
||||
@chat_channel_membership.update(status: "active")
|
||||
channel_name = @chat_channel_membership.chat_channel.channel_name
|
||||
send_chat_action_message("@#{current_user.username} joined #{@chat_channel_membership.channel_name}", current_user, @chat_channel_membership.chat_channel_id, "joined")
|
||||
flash[:settings_notice] = "Invitation to #{channel_name} accepted. It may take a moment to show up in your list."
|
||||
if previous_status == "pending"
|
||||
send_chat_action_message("@#{current_user.username} joined #{@chat_channel_membership.channel_name}", current_user, @chat_channel_membership.chat_channel_id, "joined")
|
||||
flash[:settings_notice] = "Invitation to #{channel_name} accepted. It may take a moment to show up in your list."
|
||||
else
|
||||
send_chat_action_message("@#{current_user.username} added @#{@chat_channel_membership.user.username}", current_user, @chat_channel_membership.chat_channel_id, "joined")
|
||||
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
|
||||
end
|
||||
else
|
||||
@chat_channel_membership.update(status: "rejected")
|
||||
flash[:settings_notice] = "Invitation rejected."
|
||||
|
|
|
|||
|
|
@ -13,6 +13,9 @@ class ChatChannelsController < ApplicationController
|
|||
elsif params[:state] == "pending"
|
||||
authorize ChatChannel
|
||||
render_pending_json_response
|
||||
elsif params[:state] == "joining_request"
|
||||
authorize ChatChannel
|
||||
render_joining_request_json_response
|
||||
else
|
||||
skip_authorization
|
||||
render_channels_html
|
||||
|
|
@ -31,6 +34,11 @@ class ChatChannelsController < ApplicationController
|
|||
|
||||
def update
|
||||
if ChatChannelUpdateService.new(@chat_channel, chat_channel_params).update
|
||||
if !chat_channel_params[:discoverable].to_i.zero?
|
||||
ChatChannelMembership.create(user_id: SiteConfig.mascot_user_id, chat_channel_id: @chat_channel.id, role: "member", status: "active")
|
||||
else
|
||||
ChatChannelMembership.find_by(user_id: SiteConfig.mascot_user_id)&.destroy
|
||||
end
|
||||
flash[:settings_notice] = "Channel settings updated."
|
||||
else
|
||||
default_error_message = "Channel settings updation failed. Try again later."
|
||||
|
|
@ -140,9 +148,9 @@ 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).
|
||||
where("has_unopened_messages = ? OR status = ?",
|
||||
true, "pending").
|
||||
where(has_unopened_messages: true).
|
||||
where(show_global_badge_notification: true).
|
||||
where.not(status: %w[removed_from_channel left_channel]).
|
||||
order("chat_channel_memberships.updated_at DESC")
|
||||
else
|
||||
[]
|
||||
|
|
@ -164,10 +172,16 @@ class ChatChannelsController < ApplicationController
|
|||
|
||||
def render_unopened_ids_response
|
||||
@unopened_ids = ChatChannelMembership.where(user_id: session_current_user_id).includes(:chat_channel).
|
||||
where(has_unopened_messages: true).pluck(:chat_channel_id)
|
||||
where(has_unopened_messages: true).where.not(status: %w[removed_from_channel left_channel]).pluck(:chat_channel_id)
|
||||
render json: { unopened_ids: @unopened_ids }
|
||||
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 }
|
||||
end
|
||||
|
||||
def render_channels_html
|
||||
return unless current_user && params[:slug]
|
||||
|
||||
|
|
|
|||
|
|
@ -57,8 +57,9 @@ class SearchController < ApplicationController
|
|||
end
|
||||
|
||||
def chat_channels
|
||||
search_user_id = chat_channel_params[:user_id].present? ? [current_user.id, SiteConfig.mascot_user_id] : [current_user.id]
|
||||
ccm_docs = Search::ChatChannelMembership.search_documents(
|
||||
params: chat_channel_params.merge(user_id: current_user.id).to_h,
|
||||
params: chat_channel_params.merge(user_id: search_user_id).to_h,
|
||||
)
|
||||
|
||||
render json: { result: ccm_docs }
|
||||
|
|
@ -118,6 +119,7 @@ class SearchController < ApplicationController
|
|||
channel_type
|
||||
channel_status
|
||||
status
|
||||
user_id
|
||||
]
|
||||
|
||||
params.permit(accessible)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,41 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`<Message /> should render and test snapshot 1`] = `
|
||||
<button
|
||||
class="chatchanneltabbutton"
|
||||
data-channel-id={23}
|
||||
data-channel-name="test"
|
||||
data-channel-slug="@test34"
|
||||
data-channel-status="active"
|
||||
data-content="sidecar-channel-request"
|
||||
onClick={null}
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
class="chatchanneltab chatchanneltab--"
|
||||
data-channel-id={23}
|
||||
data-channel-name="test"
|
||||
data-channel-slug="@test34"
|
||||
data-channel-status="active"
|
||||
data-content="sidecar-channel-request"
|
||||
style={
|
||||
Object {
|
||||
"border": "1px solid #00FFFF",
|
||||
"boxShadow": "3px 3px 0px #00FFFF",
|
||||
}
|
||||
}
|
||||
>
|
||||
<span
|
||||
class="chatchanneltabindicator chatchanneltabindicator--"
|
||||
data-channel-id={23}
|
||||
data-channel-slug="@test34"
|
||||
>
|
||||
<img
|
||||
alt="pic"
|
||||
class="chatchanneltabindicatordirectimage invert-channel-image"
|
||||
/>
|
||||
</span>
|
||||
test
|
||||
</span>
|
||||
</button>
|
||||
`;
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`<Message /> should render and test snapshot 1`] = `
|
||||
<div
|
||||
class="activechatchannel__activeArticle activesendrequest"
|
||||
>
|
||||
<div
|
||||
class="joining-message"
|
||||
>
|
||||
<h2>
|
||||
Hey Sarthak !
|
||||
</h2>
|
||||
<h3>
|
||||
You are not a member of this group yet. Send request to join.
|
||||
</h3>
|
||||
</div>
|
||||
<div
|
||||
class="user-picture"
|
||||
>
|
||||
<div
|
||||
class="chatmessage__profilepic"
|
||||
>
|
||||
<img
|
||||
alt="undefined profile"
|
||||
class="chatmessagebody__profileimage"
|
||||
role="presentation"
|
||||
/>
|
||||
<img
|
||||
alt="IronMan profile"
|
||||
class="chatmessagebody__profileimage"
|
||||
role="presentation"
|
||||
src="/assets/organization.svg"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="send-request"
|
||||
>
|
||||
<button
|
||||
class="cta"
|
||||
type="button"
|
||||
>
|
||||
Join IronMan
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
|
@ -9,14 +9,20 @@ exports[`<Channels /> expanded with chat channels should render and test snapsho
|
|||
id="chatchannels__channelslist"
|
||||
>
|
||||
<button
|
||||
class="chatchanneltabbutton crayons-link"
|
||||
class="chatchanneltabbutton"
|
||||
data-channel-name="channel name 1"
|
||||
data-channel-slug="@0"
|
||||
data-channel-status="active"
|
||||
data-content="sidecar-channel-request"
|
||||
onClick={[Function]}
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
class="chatchanneltab chatchanneltab--inactive chatchanneltab--old"
|
||||
data-channel-name="channel name 1"
|
||||
data-channel-slug="@0"
|
||||
data-channel-status="active"
|
||||
data-content="sidecar-channel-request"
|
||||
style={
|
||||
Object {
|
||||
"border": "1px solid undefined",
|
||||
|
|
@ -30,21 +36,27 @@ exports[`<Channels /> expanded with chat channels should render and test snapsho
|
|||
>
|
||||
<img
|
||||
alt="pic"
|
||||
class="chatchanneltabindicatorgroupimage invert-channel-image"
|
||||
class="chatchanneltabindicatordirectimage invert-channel-image"
|
||||
/>
|
||||
</span>
|
||||
channel name 1
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
class="chatchanneltabbutton crayons-link"
|
||||
class="chatchanneltabbutton"
|
||||
data-channel-name="group channel 2"
|
||||
data-channel-slug="@1"
|
||||
data-channel-status="active"
|
||||
data-content="sidecar-channel-request"
|
||||
onClick={[Function]}
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
class="chatchanneltab chatchanneltab--inactive chatchanneltab--old"
|
||||
data-channel-name="group channel 2"
|
||||
data-channel-slug="@1"
|
||||
data-channel-status="active"
|
||||
data-content="sidecar-channel-request"
|
||||
style={
|
||||
Object {
|
||||
"border": "1px solid undefined",
|
||||
|
|
@ -65,14 +77,20 @@ exports[`<Channels /> expanded with chat channels should render and test snapsho
|
|||
</span>
|
||||
</button>
|
||||
<button
|
||||
class="chatchanneltabbutton crayons-link"
|
||||
class="chatchanneltabbutton"
|
||||
data-channel-name="group channel 3"
|
||||
data-channel-slug="@2"
|
||||
data-channel-status="active"
|
||||
data-content="sidecar-channel-request"
|
||||
onClick={[Function]}
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
class="chatchanneltab chatchanneltab--inactive chatchanneltab--old"
|
||||
data-channel-name="group channel 3"
|
||||
data-channel-slug="@2"
|
||||
data-channel-status="active"
|
||||
data-content="sidecar-channel-request"
|
||||
style={
|
||||
Object {
|
||||
"border": "1px solid undefined",
|
||||
|
|
@ -86,7 +104,7 @@ exports[`<Channels /> expanded with chat channels should render and test snapsho
|
|||
>
|
||||
<img
|
||||
alt="pic"
|
||||
class="chatchanneltabindicatorgroupimage invert-channel-image"
|
||||
class="chatchanneltabindicatordirectimage invert-channel-image"
|
||||
/>
|
||||
</span>
|
||||
group channel 3
|
||||
|
|
@ -186,14 +204,20 @@ exports[`<Channels /> not expanded with chat channels should render and test sna
|
|||
id="chatchannels__channelslist"
|
||||
>
|
||||
<button
|
||||
class="chatchanneltabbutton crayons-link"
|
||||
class="chatchanneltabbutton"
|
||||
data-channel-name="channel name 1"
|
||||
data-channel-slug="@0"
|
||||
data-channel-status="active"
|
||||
data-content="sidecar-channel-request"
|
||||
onClick={[Function]}
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
class="chatchanneltab chatchanneltab--inactive chatchanneltab--old"
|
||||
data-channel-name="channel name 1"
|
||||
data-channel-slug="@0"
|
||||
data-channel-status="active"
|
||||
data-content="sidecar-channel-request"
|
||||
style={
|
||||
Object {
|
||||
"border": "1px solid undefined",
|
||||
|
|
@ -207,21 +231,27 @@ exports[`<Channels /> not expanded with chat channels should render and test sna
|
|||
>
|
||||
<img
|
||||
alt="pic"
|
||||
class="chatchanneltabindicatorgroupimage invert-channel-image"
|
||||
class="chatchanneltabindicatordirectimage invert-channel-image"
|
||||
/>
|
||||
</span>
|
||||
channel name 1
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
class="chatchanneltabbutton crayons-link"
|
||||
class="chatchanneltabbutton"
|
||||
data-channel-name="group channel 2"
|
||||
data-channel-slug="@1"
|
||||
data-channel-status="active"
|
||||
data-content="sidecar-channel-request"
|
||||
onClick={[Function]}
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
class="chatchanneltab chatchanneltab--inactive chatchanneltab--old"
|
||||
data-channel-name="group channel 2"
|
||||
data-channel-slug="@1"
|
||||
data-channel-status="active"
|
||||
data-content="sidecar-channel-request"
|
||||
style={
|
||||
Object {
|
||||
"border": "1px solid undefined",
|
||||
|
|
@ -242,14 +272,20 @@ exports[`<Channels /> not expanded with chat channels should render and test sna
|
|||
</span>
|
||||
</button>
|
||||
<button
|
||||
class="chatchanneltabbutton crayons-link"
|
||||
class="chatchanneltabbutton"
|
||||
data-channel-name="group channel 3"
|
||||
data-channel-slug="@2"
|
||||
data-channel-status="active"
|
||||
data-content="sidecar-channel-request"
|
||||
onClick={[Function]}
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
class="chatchanneltab chatchanneltab--inactive chatchanneltab--old"
|
||||
data-channel-name="group channel 3"
|
||||
data-channel-slug="@2"
|
||||
data-channel-status="active"
|
||||
data-content="sidecar-channel-request"
|
||||
style={
|
||||
Object {
|
||||
"border": "1px solid undefined",
|
||||
|
|
@ -263,7 +299,7 @@ exports[`<Channels /> not expanded with chat channels should render and test sna
|
|||
>
|
||||
<img
|
||||
alt="pic"
|
||||
class="chatchanneltabindicatorgroupimage invert-channel-image"
|
||||
class="chatchanneltabindicatordirectimage invert-channel-image"
|
||||
/>
|
||||
</span>
|
||||
group channel 3
|
||||
|
|
|
|||
|
|
@ -68,83 +68,7 @@ exports[`<Chat /> should load chat 1`] = `
|
|||
<div
|
||||
class="chatchannels__channelslist"
|
||||
id="chatchannels__channelslist"
|
||||
>
|
||||
<button
|
||||
class="chatchanneltabbutton crayons-link"
|
||||
onClick={[Function]}
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
class="chatchanneltab chatchanneltab--inactive chatchanneltab--old"
|
||||
style={
|
||||
Object {
|
||||
"border": "1px solid undefined",
|
||||
"boxShadow": "3px 3px 0px undefined",
|
||||
}
|
||||
}
|
||||
>
|
||||
<span
|
||||
class="chatchanneltabindicator chatchanneltabindicator--old"
|
||||
>
|
||||
<img
|
||||
alt="pic"
|
||||
class="chatchanneltabindicatorgroupimage invert-channel-image"
|
||||
/>
|
||||
</span>
|
||||
channel name 1
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
class="chatchanneltabbutton crayons-link"
|
||||
onClick={[Function]}
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
class="chatchanneltab chatchanneltab--inactive chatchanneltab--old"
|
||||
style={
|
||||
Object {
|
||||
"border": "1px solid undefined",
|
||||
"boxShadow": "3px 3px 0px undefined",
|
||||
}
|
||||
}
|
||||
>
|
||||
<span
|
||||
class="chatchanneltabindicator chatchanneltabindicator--old"
|
||||
>
|
||||
<img
|
||||
alt="pic"
|
||||
class="chatchanneltabindicatordirectimage"
|
||||
/>
|
||||
</span>
|
||||
group channel 2
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
class="chatchanneltabbutton crayons-link"
|
||||
onClick={[Function]}
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
class="chatchanneltab chatchanneltab--inactive chatchanneltab--old"
|
||||
style={
|
||||
Object {
|
||||
"border": "1px solid undefined",
|
||||
"boxShadow": "3px 3px 0px undefined",
|
||||
}
|
||||
}
|
||||
>
|
||||
<span
|
||||
class="chatchanneltabindicator chatchanneltabindicator--old"
|
||||
>
|
||||
<img
|
||||
alt="pic"
|
||||
class="chatchanneltabindicatorgroupimage invert-channel-image"
|
||||
/>
|
||||
</span>
|
||||
group channel 3
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
/>
|
||||
<div
|
||||
class="chatchannels__config"
|
||||
>
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ exports[`<Content /> as loading-user should render and test snapshot 1`] = `
|
|||
}
|
||||
}
|
||||
>
|
||||
|
||||
<svg
|
||||
data-content="fullscreen"
|
||||
height="24"
|
||||
|
|
@ -56,16 +57,7 @@ exports[`<Content /> as loading-user should render and test snapshot 1`] = `
|
|||
</svg>
|
||||
</button>
|
||||
<div
|
||||
style={
|
||||
Object {
|
||||
"backgroundColor": "#f5f6f7",
|
||||
"borderRadius": "500px",
|
||||
"display": "block",
|
||||
"height": "210px",
|
||||
"margin": " 15px auto",
|
||||
"width": "210px",
|
||||
}
|
||||
}
|
||||
class="loading-user"
|
||||
/>
|
||||
</div>
|
||||
`;
|
||||
|
|
|
|||
33
app/javascript/chat/__tests__/channelButton.test.jsx
Normal file
33
app/javascript/chat/__tests__/channelButton.test.jsx
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
import { h } from 'preact';
|
||||
import render from 'preact-render-to-json';
|
||||
import { shallow } from 'preact-render-spy';
|
||||
import ChannelButton from '../components/channelButton';
|
||||
|
||||
const chnl = {
|
||||
channel_name: 'test',
|
||||
channel_color: '#00FFFF',
|
||||
channel_type: 'invite_only',
|
||||
channel_modified_slug: '@test34',
|
||||
id: 34,
|
||||
chat_channel_id: 23,
|
||||
status: 'active',
|
||||
};
|
||||
|
||||
const getChannel = (channel) => <ChannelButton channel={channel} />;
|
||||
|
||||
describe('<Message />', () => {
|
||||
it('should render and test snapshot', () => {
|
||||
const tree = render(getChannel(chnl));
|
||||
expect(tree).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should have the proper elements, attributes and values', () => {
|
||||
const context = shallow(getChannel(chnl));
|
||||
expect(
|
||||
context.find('.chatchanneltabbutton').attr('data-channel-slug'),
|
||||
).toEqual(chnl.channel_modified_slug); // check user
|
||||
expect(context.find('.chatchanneltabbutton').text()).toEqual(
|
||||
chnl.channel_name,
|
||||
);
|
||||
});
|
||||
});
|
||||
28
app/javascript/chat/__tests__/channelRequest.test.jsx
Normal file
28
app/javascript/chat/__tests__/channelRequest.test.jsx
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
import { h } from 'preact';
|
||||
import render from 'preact-render-to-json';
|
||||
import { shallow } from 'preact-render-spy';
|
||||
import ChannelRequest from '../channelRequest';
|
||||
|
||||
const data = {
|
||||
user: {
|
||||
name: 'Sarthak',
|
||||
},
|
||||
channel: {
|
||||
name: 'IronMan',
|
||||
},
|
||||
};
|
||||
|
||||
const getChannelRequest = (resource) => <ChannelRequest resource={resource} />;
|
||||
|
||||
describe('<Message />', () => {
|
||||
it('should render and test snapshot', () => {
|
||||
const tree = render(getChannelRequest(data));
|
||||
expect(tree).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should have the proper elements, attributes and values', () => {
|
||||
const context = shallow(getChannelRequest(data));
|
||||
|
||||
expect(context.find('.joining-message').exists()).toEqual(true);
|
||||
});
|
||||
});
|
||||
|
|
@ -25,10 +25,12 @@ const fakeChannels = [
|
|||
slug: '0',
|
||||
channel_modified_slug: '@0',
|
||||
id: 12345,
|
||||
status: 'active',
|
||||
messages_count: 124,
|
||||
},
|
||||
{
|
||||
channel_name: 'group channel 2',
|
||||
status: 'active',
|
||||
last_opened_at: 'September 12, 2018',
|
||||
channel_users: [
|
||||
{
|
||||
|
|
@ -66,6 +68,7 @@ const fakeChannels = [
|
|||
],
|
||||
last_message_at: 'September 29, 2018',
|
||||
channel_type: 'group',
|
||||
status: 'active',
|
||||
slug: '2',
|
||||
channel_modified_slug: '@2',
|
||||
id: 67890,
|
||||
|
|
@ -103,28 +106,16 @@ describe('<Channels />', () => {
|
|||
true,
|
||||
);
|
||||
expect(
|
||||
context
|
||||
.find('.chatchannels__configmenu')
|
||||
.childAt(0)
|
||||
.text(),
|
||||
context.find('.chatchannels__configmenu').childAt(0).text(),
|
||||
).toEqual('DEV Settings');
|
||||
expect(
|
||||
context
|
||||
.find('.chatchannels__configmenu')
|
||||
.childAt(0)
|
||||
.attr('href'),
|
||||
context.find('.chatchannels__configmenu').childAt(0).attr('href'),
|
||||
).toEqual('/settings');
|
||||
expect(
|
||||
context
|
||||
.find('.chatchannels__configmenu')
|
||||
.childAt(1)
|
||||
.text(),
|
||||
context.find('.chatchannels__configmenu').childAt(1).text(),
|
||||
).toEqual('Report Abuse');
|
||||
expect(
|
||||
context
|
||||
.find('.chatchannels__configmenu')
|
||||
.childAt(1)
|
||||
.attr('href'),
|
||||
context.find('.chatchannels__configmenu').childAt(1).attr('href'),
|
||||
).toEqual('/report-abuse');
|
||||
|
||||
// welcome message should not exist because there are channels
|
||||
|
|
@ -158,28 +149,16 @@ describe('<Channels />', () => {
|
|||
true,
|
||||
);
|
||||
expect(
|
||||
context
|
||||
.find('.chatchannels__configmenu')
|
||||
.childAt(0)
|
||||
.text(),
|
||||
context.find('.chatchannels__configmenu').childAt(0).text(),
|
||||
).toEqual('DEV Settings');
|
||||
expect(
|
||||
context
|
||||
.find('.chatchannels__configmenu')
|
||||
.childAt(0)
|
||||
.attr('href'),
|
||||
context.find('.chatchannels__configmenu').childAt(0).attr('href'),
|
||||
).toEqual('/settings');
|
||||
expect(
|
||||
context
|
||||
.find('.chatchannels__configmenu')
|
||||
.childAt(1)
|
||||
.text(),
|
||||
context.find('.chatchannels__configmenu').childAt(1).text(),
|
||||
).toEqual('Report Abuse');
|
||||
expect(
|
||||
context
|
||||
.find('.chatchannels__configmenu')
|
||||
.childAt(1)
|
||||
.attr('href'),
|
||||
context.find('.chatchannels__configmenu').childAt(1).attr('href'),
|
||||
).toEqual('/report-abuse');
|
||||
});
|
||||
});
|
||||
|
|
@ -199,30 +178,6 @@ describe('<Channels />', () => {
|
|||
expect(context.find('.chatchannels__channelslist').exists()).toEqual(
|
||||
true,
|
||||
);
|
||||
expect(
|
||||
context
|
||||
.find('.chatchannels__channelslist')
|
||||
.childAt(2)
|
||||
.attr('data-channel-slug'),
|
||||
).toEqual('@1'); // check user
|
||||
expect(
|
||||
context
|
||||
.find('.chatchannels__channelslist')
|
||||
.childAt(2)
|
||||
.text(),
|
||||
).toEqual('group channel 2'); // ensure user has no text
|
||||
expect(
|
||||
context
|
||||
.find('.chatchannels__channelslist')
|
||||
.childAt(1)
|
||||
.text(),
|
||||
).toEqual(fakeChannels[0].channel_name);
|
||||
expect(
|
||||
context
|
||||
.find('.chatchannels__channelslist')
|
||||
.childAt(3)
|
||||
.text(),
|
||||
).toEqual(fakeChannels[2].channel_name);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ export function getAllMessages(channelId, messageOffset, successCb, failureCb) {
|
|||
'Content-Type': 'application/json',
|
||||
credentials: 'same-origin',
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then((response) => response.json())
|
||||
.then(successCb)
|
||||
.catch(failureCb);
|
||||
}
|
||||
|
|
@ -29,7 +29,7 @@ export function sendMessage(messageObject, successCb, failureCb) {
|
|||
}),
|
||||
credentials: 'same-origin',
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then((response) => response.json())
|
||||
.then(successCb)
|
||||
.catch(failureCb);
|
||||
}
|
||||
|
|
@ -51,7 +51,7 @@ export function editMessage(editedMessage, successCb, failureCb) {
|
|||
}),
|
||||
credentials: 'same-origin',
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then((response) => response.json())
|
||||
.then(successCb)
|
||||
.catch(failureCb);
|
||||
}
|
||||
|
|
@ -67,7 +67,7 @@ export function sendOpen(activeChannelId, successCb, failureCb) {
|
|||
body: JSON.stringify({}),
|
||||
credentials: 'same-origin',
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then((response) => response.json())
|
||||
.then(successCb)
|
||||
.catch(failureCb);
|
||||
}
|
||||
|
|
@ -92,7 +92,7 @@ export function conductModeration(
|
|||
}),
|
||||
credentials: 'same-origin',
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then((response) => response.json())
|
||||
.then(successCb)
|
||||
.catch(failureCb);
|
||||
}
|
||||
|
|
@ -100,7 +100,7 @@ export function conductModeration(
|
|||
export function getChannels(
|
||||
query,
|
||||
retrievalID,
|
||||
props,
|
||||
searchType,
|
||||
paginationNumber,
|
||||
additionalFilters,
|
||||
successCb,
|
||||
|
|
@ -114,14 +114,16 @@ export function getChannels(
|
|||
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 => {
|
||||
return responsePromise.then((response) => {
|
||||
const channels = response.result;
|
||||
if (
|
||||
retrievalID === null ||
|
||||
channels.filter(e => e.chat_channel_id === retrievalID).length === 1
|
||||
channels.filter((e) => e.chat_channel_id === retrievalID).length === 1
|
||||
) {
|
||||
successCb(channels, query);
|
||||
} else {
|
||||
|
|
@ -133,8 +135,8 @@ export function getChannels(
|
|||
credentials: 'same-origin',
|
||||
},
|
||||
)
|
||||
.then(individualResponse => individualResponse.json())
|
||||
.then(json => {
|
||||
.then((individualResponse) => individualResponse.json())
|
||||
.then((json) => {
|
||||
channels.unshift(json);
|
||||
successCb(channels, query);
|
||||
});
|
||||
|
|
@ -146,8 +148,8 @@ export function getUnopenedChannelIds(successCb) {
|
|||
fetch('/chat_channels?state=unopened_ids', {
|
||||
credentials: 'same-origin',
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(json => {
|
||||
.then((response) => response.json())
|
||||
.then((json) => {
|
||||
successCb(json.unopened_ids);
|
||||
});
|
||||
}
|
||||
|
|
@ -158,7 +160,7 @@ export function getContent(url, successCb, failureCb) {
|
|||
'Content-Type': 'application/json',
|
||||
credentials: 'same-origin',
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then((response) => response.json())
|
||||
.then(successCb)
|
||||
.catch(failureCb);
|
||||
}
|
||||
|
|
@ -169,7 +171,7 @@ export function getJSONContents(url, successCb, failureCb) {
|
|||
'Content-Type': 'application/json',
|
||||
credentials: 'same-origin',
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then((response) => response.json())
|
||||
.then(successCb)
|
||||
.catch(failureCb);
|
||||
}
|
||||
|
|
@ -180,7 +182,7 @@ export function getChannelInvites(successCb, failureCb) {
|
|||
'Content-Type': 'application/json',
|
||||
credentials: 'same-origin',
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then((response) => response.json())
|
||||
.then(successCb)
|
||||
.catch(failureCb);
|
||||
}
|
||||
|
|
@ -200,7 +202,7 @@ export function sendChannelInviteAction(id, action, successCb, failureCb) {
|
|||
}),
|
||||
credentials: 'same-origin',
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then((response) => response.json())
|
||||
.then(successCb)
|
||||
.catch(failureCb);
|
||||
}
|
||||
|
|
@ -220,7 +222,27 @@ export function deleteMessage(messageId, successCb, failureCb) {
|
|||
}),
|
||||
credentials: 'same-origin',
|
||||
})
|
||||
.then(response => response.json())
|
||||
.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);
|
||||
}
|
||||
|
|
|
|||
53
app/javascript/chat/channelRequest.jsx
Normal file
53
app/javascript/chat/channelRequest.jsx
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
import { h } from 'preact';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
const ChannelRequest = ({ resource: data, handleJoiningRequest }) => (
|
||||
<div className="activechatchannel__activeArticle activesendrequest">
|
||||
<div className="joining-message">
|
||||
<h2>Hey {data.user.name} !</h2>
|
||||
<h3>You are not a member of this group yet. Send request to join.</h3>
|
||||
</div>
|
||||
<div className="user-picture">
|
||||
<div className="chatmessage__profilepic">
|
||||
<img
|
||||
role="presentation"
|
||||
className="chatmessagebody__profileimage"
|
||||
src={data.user.profile_image_90}
|
||||
alt={`${data.user.username} profile`}
|
||||
/>
|
||||
<img
|
||||
role="presentation"
|
||||
className="chatmessagebody__profileimage"
|
||||
src="/assets/organization.svg"
|
||||
alt={`${data.channel.name} profile`}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="send-request">
|
||||
{data.channel.status !== 'joining_request' ? (
|
||||
<button
|
||||
type="button"
|
||||
className="cta"
|
||||
onClick={handleJoiningRequest}
|
||||
data-channel-id={data.channel.id}
|
||||
>
|
||||
{' '}
|
||||
Join {data.channel.name}{' '}
|
||||
</button>
|
||||
) : (
|
||||
<button type="button" className="cta">
|
||||
{' '}
|
||||
Requested Already{' '}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
ChannelRequest.propTypes = {
|
||||
resource: PropTypes.shape({
|
||||
data: PropTypes.object,
|
||||
}).isRequired,
|
||||
handleJoiningRequest: PropTypes.func.isRequired,
|
||||
};
|
||||
export default ChannelRequest;
|
||||
|
|
@ -2,6 +2,8 @@ import { h } from 'preact';
|
|||
import PropTypes from 'prop-types';
|
||||
// eslint-disable-next-line import/no-unresolved
|
||||
import ConfigImage from 'images/three-dots.svg';
|
||||
import ChannelButton from './components/channelButton';
|
||||
import { channelSorter } from './util';
|
||||
|
||||
const Channels = ({
|
||||
activeChannelId,
|
||||
|
|
@ -11,53 +13,46 @@ const Channels = ({
|
|||
expanded,
|
||||
filterQuery,
|
||||
channelsLoaded,
|
||||
currentUserId,
|
||||
triggerActiveContent,
|
||||
}) => {
|
||||
const channels = chatChannels.map(channel => {
|
||||
const sortedChatChannels = channelSorter(
|
||||
chatChannels,
|
||||
currentUserId,
|
||||
filterQuery,
|
||||
);
|
||||
const discoverableChannels = sortedChatChannels.discoverableChannels.map(
|
||||
(channel) => {
|
||||
return (
|
||||
<ChannelButton
|
||||
channel={channel}
|
||||
discoverableChannel
|
||||
triggerActiveContent={triggerActiveContent}
|
||||
/>
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
const channels = sortedChatChannels.activeChannels.map((channel) => {
|
||||
const isActive = parseInt(activeChannelId, 10) === channel.chat_channel_id;
|
||||
const isUnopened =
|
||||
!isActive && unopenedChannelIds.includes(channel.chat_channel_id);
|
||||
let newMessagesIndicator = isUnopened ? 'new' : 'old';
|
||||
const newMessagesIndicator = isUnopened ? 'new' : 'old';
|
||||
const otherClassname = isActive
|
||||
? 'chatchanneltab--active'
|
||||
: 'chatchanneltab--inactive';
|
||||
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
key={channel.id}
|
||||
className="chatchanneltabbutton crayons-link"
|
||||
onClick={handleSwitchChannel}
|
||||
data-channel-id={channel.chat_channel_id}
|
||||
data-channel-slug={channel.channel_modified_slug}
|
||||
>
|
||||
<span
|
||||
className={`chatchanneltab ${otherClassname} chatchanneltab--${newMessagesIndicator}`}
|
||||
data-channel-id={channel.chat_channel_id}
|
||||
data-channel-slug={channel.channel_modified_slug}
|
||||
style={{
|
||||
border: `1px solid ${channel.channel_color}`,
|
||||
boxShadow: `3px 3px 0px ${channel.channel_color}`,
|
||||
}}
|
||||
>
|
||||
<span
|
||||
data-channel-slug={channel.channel_modified_slug}
|
||||
className={`chatchanneltabindicator chatchanneltabindicator--${newMessagesIndicator}`}
|
||||
data-channel-id={channel.chat_channel_id}
|
||||
>
|
||||
<img
|
||||
src={channel.channel_image}
|
||||
alt="pic"
|
||||
className={
|
||||
channel.channel_type === 'direct'
|
||||
? 'chatchanneltabindicatordirectimage'
|
||||
: 'chatchanneltabindicatorgroupimage invert-channel-image'
|
||||
}
|
||||
/>
|
||||
</span>
|
||||
{isUnopened ? <span class="crayons-indicator crayons-indicator--accent crayons-indicator--bullet"></span> : ''}{channel.channel_name}
|
||||
</span>
|
||||
</button>
|
||||
<ChannelButton
|
||||
channel={channel}
|
||||
newMessagesIndicator={newMessagesIndicator}
|
||||
otherClassname={otherClassname}
|
||||
handleSwitchChannel={handleSwitchChannel}
|
||||
isUnopened={isUnopened}
|
||||
/>
|
||||
);
|
||||
});
|
||||
console.log(channels);
|
||||
let topNotice = '';
|
||||
if (
|
||||
expanded &&
|
||||
|
|
@ -103,6 +98,16 @@ const Channels = ({
|
|||
>
|
||||
{topNotice}
|
||||
{channels}
|
||||
{discoverableChannels.length > 0 ? (
|
||||
<div>
|
||||
<span className="crayons-indicator crayons-indicator--">
|
||||
Global Channel Search
|
||||
</span>
|
||||
{discoverableChannels}
|
||||
</div>
|
||||
) : (
|
||||
''
|
||||
)}
|
||||
{channelsListFooter}
|
||||
</div>
|
||||
{configFooter}
|
||||
|
|
@ -115,9 +120,11 @@ Channels.propTypes = {
|
|||
chatChannels: PropTypes.arrayOf(PropTypes.objectOf()).isRequired,
|
||||
unopenedChannelIds: PropTypes.arrayOf().isRequired,
|
||||
handleSwitchChannel: PropTypes.func.isRequired,
|
||||
triggerActiveContent: PropTypes.func.isRequired,
|
||||
expanded: PropTypes.bool.isRequired,
|
||||
filterQuery: PropTypes.string.isRequired,
|
||||
channelsLoaded: PropTypes.bool.isRequired,
|
||||
currentUserId: PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
export default Channels;
|
||||
|
|
|
|||
|
|
@ -14,8 +14,15 @@ import {
|
|||
sendChannelInviteAction,
|
||||
deleteMessage,
|
||||
editMessage,
|
||||
sendChannelRequest,
|
||||
} from './actions';
|
||||
import { hideMessages, scrollToBottom, setupObserver } from './util';
|
||||
import {
|
||||
hideMessages,
|
||||
scrollToBottom,
|
||||
setupObserver,
|
||||
getCurrentUser,
|
||||
channelSorter,
|
||||
} from './util';
|
||||
import Alert from './alert';
|
||||
import Channels from './channels';
|
||||
import Compose from './compose';
|
||||
|
|
@ -124,7 +131,7 @@ export default class Chat extends Component {
|
|||
getChannels(
|
||||
'',
|
||||
activeChannelId,
|
||||
this.props,
|
||||
'',
|
||||
channelPaginationNum,
|
||||
filters,
|
||||
this.loadChannels,
|
||||
|
|
@ -165,9 +172,11 @@ export default class Chat extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
filterForActiveChannel = (channels, id) =>
|
||||
filterForActiveChannel = (channels, id, currentUserId) =>
|
||||
channels.filter(
|
||||
(channel) => channel.chat_channel_id === parseInt(id, 10),
|
||||
(channel) =>
|
||||
channel.chat_channel_id === parseInt(id, 10) &&
|
||||
channel.viewable_by === parseInt(currentUserId, 10),
|
||||
)[0];
|
||||
|
||||
subscribePusher = (channelName) => {
|
||||
|
|
@ -271,7 +280,7 @@ export default class Chat extends Component {
|
|||
};
|
||||
|
||||
loadPaginatedChannels = (channels) => {
|
||||
const state = this.state;
|
||||
const {state} = this;
|
||||
const currentChannels = state.chatChannels;
|
||||
const currentChannelIds = currentChannels.map((channel) => channel.id);
|
||||
const newChannels = currentChannels;
|
||||
|
|
@ -408,8 +417,10 @@ export default class Chat extends Component {
|
|||
unopenedChannelIds,
|
||||
} = this.state;
|
||||
const receivedChatChannelId = message.chat_channel_id;
|
||||
const messageList = document.getElementById('messagelist')
|
||||
const nearBottom = (messageList.scrollTop + messageList.offsetHeight + 400 > messageList.scrollHeight)
|
||||
const messageList = document.getElementById('messagelist');
|
||||
const nearBottom =
|
||||
messageList.scrollTop + messageList.offsetHeight + 400 >
|
||||
messageList.scrollHeight;
|
||||
if (nearBottom) {
|
||||
scrollToBottom();
|
||||
}
|
||||
|
|
@ -431,7 +442,9 @@ export default class Chat extends Component {
|
|||
}
|
||||
}
|
||||
const newShowAlert =
|
||||
activeChannelId === receivedChatChannelId ? { showAlert: !nearBottom } : {};
|
||||
activeChannelId === receivedChatChannelId
|
||||
? { showAlert: !nearBottom }
|
||||
: {};
|
||||
let newMessageChannelIndex = 0;
|
||||
let newMessageChannel = null;
|
||||
const newChannelsObj = chatChannels.map((channel, index) => {
|
||||
|
|
@ -506,7 +519,7 @@ export default class Chat extends Component {
|
|||
getChannels(
|
||||
filterQuery,
|
||||
activeChannelId,
|
||||
this.props,
|
||||
'',
|
||||
channelPaginationNum,
|
||||
filters,
|
||||
this.loadPaginatedChannels,
|
||||
|
|
@ -546,17 +559,28 @@ export default class Chat extends Component {
|
|||
e.preventDefault();
|
||||
}
|
||||
}
|
||||
if (leftPressed && activeContent[activeChannelId] && e.target.value === '' && document.getElementById('activecontent-iframe')) {
|
||||
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
|
||||
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 === '') {
|
||||
if (
|
||||
rightPressed &&
|
||||
!activeContent[activeChannelId] &&
|
||||
e.target.value === ''
|
||||
) {
|
||||
e.preventDefault();
|
||||
const richLinks = document.querySelectorAll(".chatchannels__richlink");
|
||||
const richLinks = document.querySelectorAll('.chatchannels__richlink');
|
||||
if (richLinks.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -570,9 +594,11 @@ export default class Chat extends Component {
|
|||
}
|
||||
if (escPressed && activeContent[activeChannelId]) {
|
||||
this.setActiveContentState(activeChannelId, null);
|
||||
this.setState({fullscreenContent: null, expanded: window.innerWidth > 600});
|
||||
this.setState({
|
||||
fullscreenContent: null,
|
||||
expanded: window.innerWidth > 600,
|
||||
});
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
handleKeyDownEdit = (e) => {
|
||||
|
|
@ -620,7 +646,7 @@ export default class Chat extends Component {
|
|||
} else if (message.startsWith('/play ')) {
|
||||
const messageObject = {
|
||||
activeChannelId,
|
||||
message: message,
|
||||
message,
|
||||
mentionedUsersId: this.getMentionedUsers(message),
|
||||
};
|
||||
sendMessage(messageObject, this.handleSuccess, this.handleFailure);
|
||||
|
|
@ -637,7 +663,7 @@ export default class Chat extends Component {
|
|||
type_of: 'loading-post',
|
||||
});
|
||||
this.setActiveContent({
|
||||
path: '/search?q=' + message.replace('/search ', ''),
|
||||
path: `/search?q=${message.replace('/search ', '')}`,
|
||||
type_of: 'article',
|
||||
});
|
||||
} else if (message.startsWith('/s ')) {
|
||||
|
|
@ -645,7 +671,7 @@ export default class Chat extends Component {
|
|||
type_of: 'loading-post',
|
||||
});
|
||||
this.setActiveContent({
|
||||
path: '/search?q=' + message.replace('/s ', ''),
|
||||
path: `/search?q=${message.replace('/s ', '')}`,
|
||||
type_of: 'article',
|
||||
});
|
||||
} else if (message.startsWith('/')) {
|
||||
|
|
@ -672,7 +698,7 @@ export default class Chat extends Component {
|
|||
message,
|
||||
mentionedUsersId: this.getMentionedUsers(message),
|
||||
};
|
||||
this.setState({scrolled: false, showAlert: false})
|
||||
this.setState({ scrolled: false, showAlert: false });
|
||||
sendMessage(messageObject, this.handleSuccess, this.handleFailure);
|
||||
}
|
||||
};
|
||||
|
|
@ -695,6 +721,7 @@ export default class Chat extends Component {
|
|||
isMobileDevice,
|
||||
unopenedChannelIds,
|
||||
activeChannelId,
|
||||
currentUserId,
|
||||
} = this.state;
|
||||
const newUnopenedChannelIds = unopenedChannelIds;
|
||||
const index = newUnopenedChannelIds.indexOf(id);
|
||||
|
|
@ -702,7 +729,11 @@ export default class Chat extends Component {
|
|||
newUnopenedChannelIds.splice(index, 1);
|
||||
}
|
||||
this.setState({
|
||||
activeChannel: this.filterForActiveChannel(chatChannels, id),
|
||||
activeChannel: this.filterForActiveChannel(
|
||||
chatChannels,
|
||||
id,
|
||||
currentUserId,
|
||||
),
|
||||
activeChannelId: parseInt(id, 10),
|
||||
scrolled: false,
|
||||
showAlert: false,
|
||||
|
|
@ -805,16 +836,25 @@ export default class Chat extends Component {
|
|||
e.stopPropagation();
|
||||
|
||||
const { activeChannelId, activeChannel } = this.state;
|
||||
if (target.dataset.content.startsWith('chat_channels/')) {
|
||||
if (content.startsWith('chat_channels/')) {
|
||||
this.setActiveContentState(activeChannelId, {
|
||||
type_of: 'loading-user',
|
||||
});
|
||||
getContent(
|
||||
`/${target.dataset.content}/channel_info`,
|
||||
this.setActiveContent,
|
||||
null,
|
||||
);
|
||||
} else if (target.dataset.content === 'sidecar_all') {
|
||||
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_all') {
|
||||
this.setActiveContentState(activeChannelId, {
|
||||
type_of: 'loading-post',
|
||||
});
|
||||
|
|
@ -847,11 +887,17 @@ export default class Chat extends Component {
|
|||
});
|
||||
} else if (target.dataset.content === 'exit') {
|
||||
this.setActiveContentState(activeChannelId, null);
|
||||
this.setState({ fullscreenContent: null, expanded: window.innerWidth > 600 });
|
||||
this.setState({
|
||||
fullscreenContent: null,
|
||||
expanded: window.innerWidth > 600,
|
||||
});
|
||||
} else if (target.dataset.content === 'fullscreen') {
|
||||
const mode =
|
||||
this.state.fullscreenContent === 'sidecar' ? null : 'sidecar';
|
||||
this.setState({ fullscreenContent: mode, expanded: (mode === null || window.innerWidth > 1600) });
|
||||
this.setState({
|
||||
fullscreenContent: mode,
|
||||
expanded: mode === null || window.innerWidth > 1600,
|
||||
});
|
||||
}
|
||||
}
|
||||
document.getElementById('messageform').focus();
|
||||
|
|
@ -919,7 +965,18 @@ export default class Chat extends Component {
|
|||
fetchingPaginatedChannels: false,
|
||||
});
|
||||
const filters = type === 'all' ? {} : { filters: `channel_type:${type}` };
|
||||
getChannels(filterQuery, null, this.props, 0, filters, this.loadChannels);
|
||||
if (filterQuery && type !== 'direct') {
|
||||
getChannels(
|
||||
filterQuery,
|
||||
null,
|
||||
'discoverable',
|
||||
0,
|
||||
filters,
|
||||
this.loadChannels,
|
||||
);
|
||||
} else {
|
||||
getChannels(filterQuery, null, '', 0, filters, this.loadChannels);
|
||||
}
|
||||
};
|
||||
|
||||
handleFailure = (err) => {
|
||||
|
|
@ -1021,14 +1078,18 @@ export default class Chat extends Component {
|
|||
channelTypeFilter === 'all'
|
||||
? {}
|
||||
: { filters: `channel_type:${channelTypeFilter}` };
|
||||
getChannels(
|
||||
e.target.value,
|
||||
null,
|
||||
this.props,
|
||||
0,
|
||||
filters,
|
||||
this.loadChannels,
|
||||
);
|
||||
if (e.target.value) {
|
||||
getChannels(
|
||||
e.target.value,
|
||||
null,
|
||||
'discoverable',
|
||||
0,
|
||||
filters,
|
||||
this.loadChannels,
|
||||
);
|
||||
} else {
|
||||
getChannels(e.target.value, null, '', 0, filters, this.loadChannels);
|
||||
}
|
||||
};
|
||||
|
||||
toggleExpand = () => {
|
||||
|
|
@ -1053,6 +1114,9 @@ export default class Chat extends Component {
|
|||
setTimeout(function () {
|
||||
document.getElementById('chatchannelsearchbar').focus();
|
||||
}, 100);
|
||||
} else {
|
||||
getChannels('', null, '', 0, '', this.loadChannels);
|
||||
this.setState({ filterQuery: '' });
|
||||
}
|
||||
this.setState({ searchShowing: !this.state.searchShowing });
|
||||
};
|
||||
|
|
@ -1156,31 +1220,33 @@ export default class Chat extends Component {
|
|||
channelsLoaded={state.channelsLoaded}
|
||||
filterQuery={state.filterQuery}
|
||||
expanded={state.expanded}
|
||||
currentUserId={state.currentUserId}
|
||||
triggerActiveContent={this.triggerActiveContent}
|
||||
/>
|
||||
{notificationsState}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<div className="chat__channels">
|
||||
{notificationsButton}
|
||||
<button
|
||||
className="chat__channelstogglebutt"
|
||||
onClick={this.toggleExpand}
|
||||
style={{ width: '100%' }}
|
||||
type="button"
|
||||
>
|
||||
{'>'}
|
||||
</button>
|
||||
<Channels
|
||||
activeChannelId={state.activeChannelId}
|
||||
chatChannels={state.chatChannels}
|
||||
unopenedChannelIds={state.unopenedChannelIds}
|
||||
handleSwitchChannel={this.handleSwitchChannel}
|
||||
expanded={state.expanded}
|
||||
/>
|
||||
{notificationsState}
|
||||
</div>
|
||||
<div className="chat__channels">
|
||||
{notificationsButton}
|
||||
<button
|
||||
className="chat__channelstogglebutt"
|
||||
onClick={this.toggleExpand}
|
||||
style={{ width: '100%' }}
|
||||
type="button"
|
||||
>
|
||||
{'>'}
|
||||
</button>
|
||||
<Channels
|
||||
activeChannelId={state.activeChannelId}
|
||||
chatChannels={state.chatChannels}
|
||||
unopenedChannelIds={state.unopenedChannelIds}
|
||||
handleSwitchChannel={this.handleSwitchChannel}
|
||||
expanded={state.expanded}
|
||||
/>
|
||||
{notificationsState}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return '';
|
||||
|
|
@ -1320,11 +1386,18 @@ export default class Chat extends Component {
|
|||
|
||||
onTriggerVideoContent = (e) => {
|
||||
if (e.target.dataset.content === 'exit') {
|
||||
this.setState({ videoPath: null, fullscreenContent: null, expanded: window.innerWidth > 600 });
|
||||
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 > 1600 });
|
||||
this.setState({
|
||||
fullscreenContent: 'video',
|
||||
expanded: window.innerWidth > 1600,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -1551,6 +1624,21 @@ export default class Chat extends Component {
|
|||
this.setState({ showDeleteModal: false });
|
||||
};
|
||||
|
||||
handleJoiningRequest = (e) => {
|
||||
sendChannelRequest(
|
||||
e.target.dataset.channelId,
|
||||
this.handleJoiningRequestSuccess,
|
||||
null,
|
||||
);
|
||||
};
|
||||
|
||||
handleJoiningRequestSuccess = () => {
|
||||
const { activeChannelId } = this.state;
|
||||
this.setActiveContentState(activeChannelId, null);
|
||||
this.setState({ fullscreenContent: null });
|
||||
this.toggleSearchShowing();
|
||||
};
|
||||
|
||||
renderChannelHeaderInner = () => {
|
||||
const { activeChannel } = this.state;
|
||||
if (activeChannel.channel_type === 'direct') {
|
||||
|
|
|
|||
98
app/javascript/chat/components/channelButton.jsx
Normal file
98
app/javascript/chat/components/channelButton.jsx
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
import { h } from 'preact';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
const ChannelButton = ({
|
||||
channel,
|
||||
handleSwitchChannel,
|
||||
otherClassname,
|
||||
newMessagesIndicator,
|
||||
isUnopened,
|
||||
discoverableChannel,
|
||||
triggerActiveContent,
|
||||
}) => {
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
key={channel.id}
|
||||
className="chatchanneltabbutton"
|
||||
onClick={discoverableChannel ? triggerActiveContent : handleSwitchChannel}
|
||||
data-content="sidecar-channel-request"
|
||||
data-channel-id={channel.chat_channel_id}
|
||||
data-channel-slug={channel.channel_modified_slug}
|
||||
data-channel-status={channel.status}
|
||||
data-channel-name={channel.channel_name}
|
||||
>
|
||||
<span
|
||||
className={
|
||||
discoverableChannel
|
||||
? 'chatchanneltab chatchanneltab--inactive'
|
||||
: `chatchanneltab ${otherClassname} chatchanneltab--${newMessagesIndicator}`
|
||||
}
|
||||
data-channel-id={channel.chat_channel_id}
|
||||
data-content="sidecar-channel-request"
|
||||
data-channel-slug={channel.channel_modified_slug}
|
||||
data-channel-status={channel.status}
|
||||
data-channel-name={channel.channel_name}
|
||||
style={{
|
||||
border: `1px solid ${channel.channel_color}`,
|
||||
boxShadow: `3px 3px 0px ${channel.channel_color}`,
|
||||
}}
|
||||
>
|
||||
<span
|
||||
data-channel-slug={channel.channel_modified_slug}
|
||||
className={
|
||||
discoverableChannel
|
||||
? 'chatchanneltabindicator'
|
||||
: `chatchanneltabindicator chatchanneltabindicator--${newMessagesIndicator}`
|
||||
}
|
||||
data-channel-id={channel.chat_channel_id}
|
||||
>
|
||||
<img
|
||||
src={channel.channel_image}
|
||||
alt="pic"
|
||||
className={
|
||||
channel.channel_type === 'direct'
|
||||
? 'chatchanneltabindicatordirectimage'
|
||||
: 'chatchanneltabindicatordirectimage invert-channel-image'
|
||||
}
|
||||
/>
|
||||
</span>
|
||||
{isUnopened ? (
|
||||
<span className="crayons-indicator crayons-indicator--accent crayons-indicator--bullet" />
|
||||
) : (
|
||||
''
|
||||
)}
|
||||
{channel.channel_name}
|
||||
</span>
|
||||
</button>
|
||||
);
|
||||
};
|
||||
|
||||
ChannelButton.propTypes = {
|
||||
channel: PropTypes.shape({
|
||||
channel_name: PropTypes.string,
|
||||
channel_color: PropTypes.string,
|
||||
channel_type: PropTypes.string,
|
||||
channel_modified_slug: PropTypes.string,
|
||||
id: PropTypes.number,
|
||||
chat_channel_id: PropTypes.number,
|
||||
status: PropTypes.string,
|
||||
channel_image: PropTypes.string,
|
||||
}).isRequired,
|
||||
discoverableChannel: PropTypes.bool,
|
||||
handleSwitchChannel: PropTypes.func,
|
||||
triggerActiveContent: PropTypes.func,
|
||||
newMessagesIndicator: PropTypes.string,
|
||||
otherClassname: PropTypes.string,
|
||||
isUnopened: PropTypes.string,
|
||||
};
|
||||
|
||||
ChannelButton.defaultProps = {
|
||||
otherClassname: '',
|
||||
isUnopened: '',
|
||||
newMessagesIndicator: '',
|
||||
discoverableChannel: false,
|
||||
handleSwitchChannel: null,
|
||||
triggerActiveContent: null,
|
||||
};
|
||||
export default ChannelButton;
|
||||
|
|
@ -1,8 +1,7 @@
|
|||
import { h, Component } from 'preact';
|
||||
import PropTypes from 'prop-types';
|
||||
import Article from './article';
|
||||
|
||||
|
||||
import ChannelRequest from './channelRequest';
|
||||
export default class Content extends Component {
|
||||
static propTypes = {
|
||||
resource: PropTypes.object,
|
||||
|
|
@ -12,28 +11,53 @@ export default class Content extends Component {
|
|||
};
|
||||
|
||||
render() {
|
||||
const { onTriggerContent, fullscreen } = this.props;
|
||||
if (!this.props.resource) {
|
||||
return '';
|
||||
}
|
||||
const fullScreenIcon = this.props.fullscreen ? <svg data-content="fullscreen" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path data-content="fullscreen" fill="none" d="M0 0h24v24H0z"/><path data-content="fullscreen" d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"/></svg> : <svg data-content="fullscreen" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path data-content="fullscreen" fill="none" d="M0 0h24v24H0z"/><path data-content="fullscreen" d="M20 3h2v6h-2V5h-4V3h4zM4 3h4v2H4v4H2V3h2zm16 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"/></svg>;
|
||||
const smartSvgIcon = (content, d) => (
|
||||
<svg
|
||||
data-content={content}
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
width="24"
|
||||
height="24"
|
||||
>
|
||||
<path data-content={content} fill="none" d="M0 0h24v24H0z" />
|
||||
<path data-content={content} d={d} />
|
||||
</svg>
|
||||
);
|
||||
|
||||
return (
|
||||
<div
|
||||
className="activechatchannel__activecontent activechatchannel__activecontent--sidecar"
|
||||
id="chat_activecontent"
|
||||
onClick={this.props.onTriggerContent}
|
||||
onClick={onTriggerContent}
|
||||
>
|
||||
<button
|
||||
className="activechatchannel__activecontentexitbutton crayons-btn crayons-btn--secondary"
|
||||
data-content="exit"
|
||||
>
|
||||
<svg data-content="exit" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path data-content="exit" fill="none" d="M0 0h24v24H0z"/><path data-content="exit" 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"/></svg>
|
||||
{smartSvgIcon(
|
||||
'exit',
|
||||
'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',
|
||||
)}
|
||||
</button>
|
||||
<button
|
||||
className="activechatchannel__activecontentexitbutton activechatchannel__activecontentexitbutton--fullscreen crayons-btn crayons-btn--secondary"
|
||||
data-content="fullscreen"
|
||||
style={{left: "39px"}}
|
||||
style={{ left: '39px' }}
|
||||
>
|
||||
{fullScreenIcon}
|
||||
{' '}
|
||||
{fullscreen
|
||||
? smartSvgIcon(
|
||||
'fullscreen',
|
||||
'M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z',
|
||||
)
|
||||
: smartSvgIcon(
|
||||
'fullscreen',
|
||||
'M20 3h2v6h-2V5h-4V3h4zM4 3h4v2H4v4H2V3h2zm16 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z',
|
||||
)}
|
||||
</button>
|
||||
{display(this.props)}
|
||||
</div>
|
||||
|
|
@ -42,21 +66,19 @@ export default class Content extends Component {
|
|||
}
|
||||
|
||||
function display(props) {
|
||||
if (props.resource.type_of === 'loading-user') {
|
||||
const { resource } = props;
|
||||
if (resource.type_of === 'loading-user') {
|
||||
return <div className="loading-user" />;
|
||||
}
|
||||
if (resource.type_of === 'article') {
|
||||
return <Article resource={resource} />;
|
||||
}
|
||||
if (resource.type_of === 'channel-request') {
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
height: '210px',
|
||||
width: '210px',
|
||||
margin: ' 15px auto',
|
||||
display: 'block',
|
||||
borderRadius: '500px',
|
||||
backgroundColor: '#f5f6f7',
|
||||
}}
|
||||
<ChannelRequest
|
||||
resource={resource.data}
|
||||
handleJoiningRequest={resource.handleJoiningRequest}
|
||||
/>
|
||||
);
|
||||
}
|
||||
if (props.resource.type_of === 'article') {
|
||||
return <Article resource={props.resource} />;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,6 +30,11 @@ const getWaitOnUserDataHandler = ({ resolve, reject, waitTime = 20 }) => {
|
|||
};
|
||||
};
|
||||
|
||||
export const getCurrentUser = () => {
|
||||
const { user } = document.body.dataset;
|
||||
return JSON.parse(user);
|
||||
};
|
||||
|
||||
export function getUserDataAndCsrfToken() {
|
||||
return new Promise((resolve, reject) => {
|
||||
getWaitOnUserDataHandler({ resolve, reject })();
|
||||
|
|
@ -52,7 +57,7 @@ export function setupObserver(callback) {
|
|||
export function hideMessages(messages, userId) {
|
||||
const cleanedMessages = Object.keys(messages).reduce(
|
||||
(accumulator, channelId) => {
|
||||
const newMessages = messages[channelId].map(message => {
|
||||
const newMessages = messages[channelId].map((message) => {
|
||||
if (message.user_id === userId) {
|
||||
const messageClone = Object.assign({ type: 'hidden' }, message);
|
||||
messageClone.message = '<message removed>';
|
||||
|
|
@ -79,3 +84,26 @@ export function adjustTimestamp(timestamp) {
|
|||
time = new Intl.DateTimeFormat('en-US', options).format(time);
|
||||
return time;
|
||||
}
|
||||
|
||||
export const channelSorter = (channels, currentUserId, filterQuery) => {
|
||||
const activeChannels = channels.filter(
|
||||
(channel) =>
|
||||
channel.viewable_by === currentUserId && channel.status === 'active',
|
||||
);
|
||||
const joiningChannels = channels.filter(
|
||||
(channel) => channel.status === 'joining_request',
|
||||
);
|
||||
const ChannelIds = [
|
||||
[...new Set(activeChannels.map((x) => x.chat_channel_id))],
|
||||
[...new Set(joiningChannels.map((x) => x.chat_channel_id))],
|
||||
];
|
||||
const discoverableChannels = channels
|
||||
.filter(
|
||||
(channel) =>
|
||||
(channel.status === 'joining_request' && filterQuery) ||
|
||||
(!ChannelIds[1].includes(channel.chat_channel_id) &&
|
||||
channel.viewable_by !== currentUserId),
|
||||
)
|
||||
.filter((channel) => !ChannelIds[0].includes(channel.chat_channel_id));
|
||||
return { activeChannels, discoverableChannels };
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,12 +1,23 @@
|
|||
import { h, Component } from 'preact';
|
||||
|
||||
export default class VideoContent extends Component {
|
||||
|
||||
render() {
|
||||
if (!this.props.videoPath) {
|
||||
return ""
|
||||
return '';
|
||||
}
|
||||
const fullScreenIcon = this.props.fullscreen ? <svg data-content="fullscreen" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path data-content="fullscreen" fill="none" d="M0 0h24v24H0z"/><path data-content="fullscreen" d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"/></svg> : <svg data-content="fullscreen" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path data-content="fullscreen" fill="none" d="M0 0h24v24H0z"/><path data-content="fullscreen" d="M20 3h2v6h-2V5h-4V3h4zM4 3h4v2H4v4H2V3h2zm16 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"/></svg>;
|
||||
|
||||
const smartSvgIcon = (content, d) => (
|
||||
<svg
|
||||
data-content={content}
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
width="24"
|
||||
height="24"
|
||||
>
|
||||
<path data-content={content} fill="none" d="M0 0h24v24H0z" />
|
||||
<path data-content={content} d={d} />
|
||||
</svg>
|
||||
);
|
||||
return (
|
||||
<div
|
||||
className="activechatchannel__activecontent activechatchannel__activecontent--video"
|
||||
|
|
@ -17,14 +28,25 @@ export default class VideoContent extends Component {
|
|||
className="activechatchannel__activecontentexitbutton crayons-btn crayons-btn--secondary"
|
||||
data-content="exit"
|
||||
>
|
||||
<svg data-content="exit" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path data-content="exit" fill="none" d="M0 0h24v24H0z"/><path data-content="exit" 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"/></svg>
|
||||
{smartSvgIcon(
|
||||
'exit',
|
||||
'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',
|
||||
)}
|
||||
</button>
|
||||
<button
|
||||
className="activechatchannel__activecontentexitbutton activechatchannel__activecontentexitbutton--fullscreen crayons-btn crayons-btn--secondary"
|
||||
data-content="fullscreen"
|
||||
style={{left: "39px"}}
|
||||
style={{ left: '39px' }}
|
||||
>
|
||||
{fullScreenIcon}
|
||||
{fullscreen
|
||||
? smartSvgIcon(
|
||||
'fullscreen',
|
||||
'M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z',
|
||||
)
|
||||
: smartSvgIcon(
|
||||
'fullscreen',
|
||||
'M20 3h2v6h-2V5h-4V3h4zM4 3h4v2H4v4H2V3h2zm16 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z',
|
||||
)}
|
||||
</button>
|
||||
<iframe src={this.props.videoPath} />
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ import Chat from '../chat/chat';
|
|||
|
||||
function loadElement() {
|
||||
const root = document.getElementById('chat');
|
||||
|
||||
if (root) {
|
||||
render(<Chat {...root.dataset} />, root, root.firstChild);
|
||||
const placeholder = document.getElementById('chat_placeholder');
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ class ChatChannel < ApplicationRecord
|
|||
has_many :pending_memberships, -> { where status: "pending" }, class_name: "ChatChannelMembership", inverse_of: :chat_channel
|
||||
has_many :rejected_memberships, -> { where status: "rejected" }, class_name: "ChatChannelMembership", inverse_of: :chat_channel
|
||||
has_many :mod_memberships, -> { where role: "mod" }, class_name: "ChatChannelMembership", inverse_of: :chat_channel
|
||||
has_many :requested_memberships, -> { where status: "joining_request" }, class_name: "ChatChannelMembership", inverse_of: :chat_channel
|
||||
has_many :active_users, through: :active_memberships, class_name: "User", source: :user
|
||||
has_many :pending_users, through: :pending_memberships, class_name: "User", source: :user
|
||||
has_many :rejected_users, through: :rejected_memberships, class_name: "User", source: :user
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ class ChatChannelMembership < ApplicationRecord
|
|||
|
||||
validates :user_id, presence: true, uniqueness: { scope: :chat_channel_id }
|
||||
validates :chat_channel_id, presence: true, uniqueness: { scope: :user_id }
|
||||
validates :status, inclusion: { in: %w[active inactive pending rejected left_channel removed_from_channel] }
|
||||
validates :status, inclusion: { in: %w[active inactive pending rejected left_channel removed_from_channel joining_request] }
|
||||
validates :role, inclusion: { in: %w[member mod] }
|
||||
validate :permission
|
||||
|
||||
|
|
@ -70,6 +70,10 @@ class ChatChannelMembership < ApplicationRecord
|
|||
user_id
|
||||
end
|
||||
|
||||
def channel_discoverable
|
||||
chat_channel.discoverable
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def channel_color
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ class ChatChannelPolicy < ApplicationPolicy
|
|||
end
|
||||
|
||||
def permitted_attributes
|
||||
%i[channel_name slug command description]
|
||||
%i[channel_name slug command description discoverable]
|
||||
end
|
||||
|
||||
def create_chat?
|
||||
|
|
|
|||
|
|
@ -5,6 +5,6 @@ module Search
|
|||
attributes :id, :status, :viewable_by, :chat_channel_id, :last_opened_at,
|
||||
:channel_text, :channel_last_message_at, :channel_status,
|
||||
:channel_status, :channel_type, :channel_username, :channel_name,
|
||||
:channel_image, :channel_modified_slug, :channel_messages_count
|
||||
:channel_image, :channel_modified_slug, :channel_discoverable, :channel_messages_count
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -20,11 +20,11 @@ module Search
|
|||
|
||||
def initialize(params:)
|
||||
@params = params.deep_symbolize_keys
|
||||
@params[:viewable_by] = params[:user_id]
|
||||
@params[:viewable_by] = @params[:user_id]
|
||||
|
||||
# TODO: @mstruve: When we want to allow people like admins to
|
||||
# search ALL memberships this will need to change
|
||||
@params[:status] = "active"
|
||||
@params[:status] = %w[active joining_request]
|
||||
|
||||
build_body
|
||||
end
|
||||
|
|
@ -41,7 +41,11 @@ module Search
|
|||
FILTER_KEYS.map do |filter_key|
|
||||
next if @params[filter_key].blank? || @params[filter_key] == "all"
|
||||
|
||||
{ term: { filter_key => @params[filter_key] } }
|
||||
if %i[viewable_by status].include? filter_key
|
||||
{ terms: { filter_key => @params[filter_key] } }
|
||||
else
|
||||
{ term: { filter_key => @params[filter_key] } }
|
||||
end
|
||||
end.compact
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -37,7 +37,6 @@
|
|||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<% if @membership.role == "mod" %>
|
||||
<div class="crayons-card p-4 grid gap-2 mb-4">
|
||||
<h3>Pending Invitations</h3>
|
||||
|
|
@ -57,30 +56,66 @@
|
|||
<% end %>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<div class="crayons-card p-4 grid gap-2 mb-4">
|
||||
<h3>Joining Request</h3>
|
||||
<% @channel.requested_memberships.includes(:user).each do |requested_membership| %>
|
||||
<div class="flex items-center">
|
||||
<a href="<%= requested_membership.user.path %>">
|
||||
<span class="crayons-avatar crayons-avatar--l mr-2">
|
||||
<img src="<%= ProfileImage.new(requested_membership.user).get(width: 90) %>" class="crayons-avatar__image" />
|
||||
</span>
|
||||
<%= requested_membership.user.name %>
|
||||
</a>
|
||||
<%= form_tag "/chat_channel_memberships/add_membership", class: "inline-button-form" do %>
|
||||
<%= hidden_field_tag(:chat_channel_id, @channel.id) %>
|
||||
<%= hidden_field_tag(:membership_id, requested_membership.id) %>
|
||||
<%= hidden_field(:chat_channel_membership, :user_action, value: "accept") %>
|
||||
<button class="crayons-btn crayons-btn--icon-rounded crayons-btn--ghost" type="submit">+</button>
|
||||
<% end %>
|
||||
<%= form_tag "/chat_channel_memberships/remove_membership", class: "inline-button-form" do %>
|
||||
<%= hidden_field_tag(:status, "pending") %>
|
||||
<%= hidden_field_tag(:chat_channel_id, @channel.id) %>
|
||||
<%= hidden_field_tag(:membership_id, requested_membership.id) %>
|
||||
<button class="crayons-btn crayons-btn--icon-rounded crayons-btn--ghost" type="submit">x</button>
|
||||
<% end %>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<div class="crayons-card p-4 grid gap-2 mb-4">
|
||||
<% if @channel.private_org_channel? %>
|
||||
<p><em>Invite new members by <a href="/settings/organization">inviting them to this organization.</a></em></p>
|
||||
<% else %>
|
||||
<%= form_for ChatChannelMembership.new, html: { class: "grid gap-4" } do |f| %>
|
||||
<div class="crayons-field">
|
||||
<%= f.hidden_field :chat_channel_id, value: @channel.id %>
|
||||
<%= f.label "Usernames to Invite", class: "crayons-field__label" %>
|
||||
<%= f.text_field :invitation_usernames, placeholder: "Comma separated", class: "crayons-textfield" %>
|
||||
</div>
|
||||
<div><button type="submit" class="crayons-btn">Submit</button></div>
|
||||
<% end %>
|
||||
<%= form_for ChatChannelMembership.new, html: { class: "grid gap-4" } do |f| %>
|
||||
<div class="crayons-field">
|
||||
<%= f.hidden_field :chat_channel_id, value: @channel.id %>
|
||||
<%= f.label "Usernames to Invite", class: "crayons-field__label" %>
|
||||
<%= f.text_field :invitation_usernames, placeholder: "Comma separated", class: "crayons-textfield" %>
|
||||
</div>
|
||||
<div><button type="submit" class="crayons-btn">Submit</button></div>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<h3>Channel Settings</h3>
|
||||
<div class="crayons-card p-4 grid gap-2 mb-4">
|
||||
<h3>Channel Settings</h3>
|
||||
<%= form_for(@channel) do |f| %>
|
||||
<div class="crayons-field">
|
||||
<%= f.label :description, class: "crayons-field__label" %>
|
||||
<%= f.text_area :description, class: "crayons-textfield" %>
|
||||
</div>
|
||||
<div>
|
||||
<button type="submit" class="crayons-btn">Submit</button>
|
||||
<div class="crayons-field">
|
||||
<%= f.label :description, class: "crayons-field__label" %>
|
||||
<%= f.text_area :description, class: "crayons-textfield" %>
|
||||
</div>
|
||||
<div class="crayons-card p-4 grid gap-2 mb-4">
|
||||
<div class="crayons-field crayons-field--checkbox">
|
||||
<%= f.check_box :discoverable, class: "crayons-checkbox" %>
|
||||
<%= f.label :discoverable, "Channel Discoverable", class: "crayons-field__label" %>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<button type="submit" class="crayons-btn">Submit</button>
|
||||
</div>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
|
|
|
|||
|
|
@ -10,6 +10,10 @@
|
|||
<h2 style="font-size: 25px; text-align: center;">
|
||||
<%= link_to(@inviter.name, ApplicationController.helpers.user_url(@inviter)) %> sent you an invitation to join a Connect group on <%= community_name %>.
|
||||
</h2>
|
||||
<% elsif @membership.status == "joining_request" %>
|
||||
<h2 style="font-size: 25px; text-align: center;">
|
||||
Your request to join <%= community_name %> has been accepted.
|
||||
</h2>
|
||||
<% else %>
|
||||
<h2 style="font-size: 25px; text-align: center;">
|
||||
You have been invited to a Connect group on <%= community_name %>.
|
||||
|
|
|
|||
|
|
@ -51,6 +51,9 @@
|
|||
"channel_messages_count": {
|
||||
"type": "integer"
|
||||
},
|
||||
"channel_discoverable": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"last_indexed_at": {
|
||||
"type": "date"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -265,6 +265,8 @@ Rails.application.routes.draw do
|
|||
post "/chat_channels/create_chat" => "chat_channels#create_chat"
|
||||
post "/chat_channels/block_chat" => "chat_channels#block_chat"
|
||||
post "/chat_channel_memberships/remove_membership" => "chat_channel_memberships#remove_membership"
|
||||
post "/chat_channel_memberships/add_membership" => "chat_channel_memberships#add_membership"
|
||||
post "/join_chat_channel" => "chat_channel_memberships#join_channel"
|
||||
delete "/messages/:id" => "messages#destroy"
|
||||
patch "/messages/:id" => "messages#update"
|
||||
get "/live/:username" => "twitch_live_streams#show"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
class AddColumnDiscoverableToChatChannels < ActiveRecord::Migration[5.2]
|
||||
def change
|
||||
add_column :chat_channels, :discoverable, :boolean, :default => false
|
||||
end
|
||||
end
|
||||
|
|
@ -304,6 +304,7 @@ ActiveRecord::Schema.define(version: 2020_05_04_075409) do
|
|||
t.string "channel_type", null: false
|
||||
t.datetime "created_at", null: false
|
||||
t.string "description"
|
||||
t.boolean "discoverable", default: false
|
||||
t.datetime "last_message_at", default: "2017-01-01 05:00:00"
|
||||
t.string "slug"
|
||||
t.string "status", default: "active"
|
||||
|
|
|
|||
|
|
@ -321,4 +321,79 @@ RSpec.describe "ChatChannelMemberships", type: :request do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "POST /chat_channel_memberships/add_membership" do
|
||||
context "when user is moderator of channel" do
|
||||
it "adds requested member to join closed channel" do
|
||||
allow(Pusher).to receive(:trigger).and_return(true)
|
||||
channel = ChatChannel.first
|
||||
membership = ChatChannelMembership.last
|
||||
membership.update(status: "joining_request")
|
||||
membership.update(role: "mod")
|
||||
post "/chat_channel_memberships/add_membership", params: {
|
||||
membership_id: membership.id,
|
||||
chat_channel_id: channel.id,
|
||||
chat_channel_membership: {
|
||||
user_action: "accept"
|
||||
}
|
||||
}
|
||||
expect(ChatChannelMembership.find(membership.id).status).to eq("active")
|
||||
expect(response).to(redirect_to(edit_chat_channel_membership_path(membership.id)))
|
||||
end
|
||||
end
|
||||
|
||||
context "when user is member of channel" do
|
||||
it "raise Pundit::NotAuthorizedError" do
|
||||
expect do
|
||||
channel = ChatChannel.first
|
||||
membership = ChatChannelMembership.last
|
||||
membership.update(status: "joining_request")
|
||||
post "/chat_channel_memberships/add_membership", params: {
|
||||
membership_id: membership.id,
|
||||
chat_channel_id: channel.id,
|
||||
chat_channel_membership: {
|
||||
user_action: "accept"
|
||||
}
|
||||
}
|
||||
end.to(raise_error(Pundit::NotAuthorizedError))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "POST /join_chat_channel" do
|
||||
let(:chat_channel_membership) do
|
||||
{
|
||||
chat_channel_id: chat_channel.id
|
||||
}
|
||||
end
|
||||
|
||||
before do
|
||||
allow(Pusher).to receive(:trigger).and_return(true)
|
||||
sign_in second_user
|
||||
post "/join_chat_channel", params: { chat_channel_membership: chat_channel_membership }
|
||||
end
|
||||
|
||||
context "when user was not member of closed channel" do
|
||||
it "requested to join closed channel" do
|
||||
expect(ChatChannelMembership.last.status).to eq("joining_request")
|
||||
expect(response.status).to eq(200)
|
||||
end
|
||||
|
||||
it "returns in json" do
|
||||
expect(response.content_type).to eq("application/json")
|
||||
end
|
||||
end
|
||||
|
||||
context "when user was a member of channel, and than left channel" do
|
||||
it "requested to join closed channel" do
|
||||
ChatChannelMembership.create(chat_channel_id: chat_channel.id, user_id: second_user.id, status: "left_channel")
|
||||
expect(ChatChannelMembership.last.status).to eq("joining_request")
|
||||
expect(response.status).to eq(200)
|
||||
end
|
||||
|
||||
it "returns in json" do
|
||||
expect(response.content_type).to eq("application/json")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -51,6 +51,17 @@ RSpec.describe "ChatChannels", type: :request do
|
|||
end
|
||||
end
|
||||
|
||||
describe "get /chat_channels?state=joining_request" do
|
||||
it "returns joining request channels" do
|
||||
membership = ChatChannelMembership.create(chat_channel_id: invite_channel.id, user_id: user.id, status: "joining_request", role: "mod")
|
||||
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")
|
||||
end
|
||||
end
|
||||
|
||||
describe "get /chat_channels?state=unopened_ids" do
|
||||
it "returns unopened chat channel ids" do
|
||||
direct_channel.add_users [user]
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ RSpec.describe Search::ChatChannelMembership, type: :service do
|
|||
allow(chat_channel_membership1).to receive(:channel_text).and_return("a name")
|
||||
allow(chat_channel_membership2).to receive(:channel_text).and_return("another name and slug")
|
||||
index_documents([chat_channel_membership1, chat_channel_membership2])
|
||||
name_params = { size: 5, channel_text: "name", user_id: user.id }
|
||||
name_params = { size: 5, channel_text: "name", user_id: [user.id] }
|
||||
|
||||
chat_channel_membership_docs = described_class.search_documents(params: name_params)
|
||||
expect(chat_channel_membership_docs.count).to eq(2)
|
||||
|
|
@ -32,7 +32,7 @@ RSpec.describe Search::ChatChannelMembership, type: :service do
|
|||
new_user = create(:user)
|
||||
chat_channel_membership3 = create(:chat_channel_membership, user_id: new_user.id)
|
||||
index_documents([chat_channel_membership1, chat_channel_membership2, chat_channel_membership3])
|
||||
params = { size: 5, user_id: new_user.id }
|
||||
params = { size: 5, user_id: [new_user.id] }
|
||||
|
||||
chat_channel_membership_docs = described_class.search_documents(params: params)
|
||||
expect(chat_channel_membership_docs.count).to eq(1)
|
||||
|
|
@ -42,7 +42,7 @@ RSpec.describe Search::ChatChannelMembership, type: :service do
|
|||
it "searches by channel_status" do
|
||||
allow(chat_channel_membership1).to receive(:channel_status).and_return("popping")
|
||||
index_documents([chat_channel_membership1, chat_channel_membership2])
|
||||
params = { size: 5, channel_status: "popping", user_id: user.id }
|
||||
params = { size: 5, channel_status: "popping", user_id: [user.id] }
|
||||
|
||||
chat_channel_membership_docs = described_class.search_documents(params: params)
|
||||
expect(chat_channel_membership_docs.count).to eq(1)
|
||||
|
|
@ -52,7 +52,7 @@ RSpec.describe Search::ChatChannelMembership, type: :service do
|
|||
it "searches by channel_type" do
|
||||
allow(chat_channel_membership2).to receive(:channel_type).and_return("invite_only")
|
||||
index_documents([chat_channel_membership1, chat_channel_membership2])
|
||||
params = { size: 5, channel_type: "invite_only", user_id: user.id }
|
||||
params = { size: 5, channel_type: "invite_only", user_id: [user.id] }
|
||||
|
||||
chat_channel_membership_docs = described_class.search_documents(params: params)
|
||||
expect(chat_channel_membership_docs.count).to eq(1)
|
||||
|
|
@ -63,7 +63,7 @@ RSpec.describe Search::ChatChannelMembership, type: :service do
|
|||
chat_channel_membership1.update(status: "inactive")
|
||||
chat_channel_membership2.update(status: "active")
|
||||
index_documents([chat_channel_membership1, chat_channel_membership2])
|
||||
params = { size: 5, user_id: user.id }
|
||||
params = { size: 5, user_id: [user.id] }
|
||||
|
||||
chat_channel_membership_docs = described_class.search_documents(params: params)
|
||||
expect(chat_channel_membership_docs.count).to eq(1)
|
||||
|
|
@ -78,7 +78,7 @@ RSpec.describe Search::ChatChannelMembership, type: :service do
|
|||
chat_channel_membership1.update(status: "active")
|
||||
chat_channel_membership2.update(status: "inactive")
|
||||
index_documents([chat_channel_membership1, chat_channel_membership2])
|
||||
name_params = { size: 5, channel_text: "name", status: "active", user_id: user.id }
|
||||
name_params = { size: 5, channel_text: "name", status: "active", user_id: [user.id] }
|
||||
|
||||
chat_channel_membership_docs = described_class.search_documents(params: name_params)
|
||||
expect(chat_channel_membership_docs.count).to eq(1)
|
||||
|
|
@ -91,7 +91,7 @@ RSpec.describe Search::ChatChannelMembership, type: :service do
|
|||
allow(chat_channel_membership1).to receive(:channel_type).and_return("not_direct")
|
||||
allow(chat_channel_membership2).to receive(:channel_type).and_return("direct")
|
||||
index_documents([chat_channel_membership1, chat_channel_membership2])
|
||||
params = { size: 5, sort_by: "channel_type", sort_direction: "asc", user_id: user.id }
|
||||
params = { size: 5, sort_by: "channel_type", sort_direction: "asc", user_id: [user.id] }
|
||||
|
||||
chat_channel_membership_docs = described_class.search_documents(params: params)
|
||||
expect(chat_channel_membership_docs.count).to eq(2)
|
||||
|
|
@ -103,7 +103,7 @@ RSpec.describe Search::ChatChannelMembership, type: :service do
|
|||
allow(chat_channel_membership1).to receive(:channel_last_message_at).and_return(Time.current)
|
||||
allow(chat_channel_membership2).to receive(:channel_last_message_at).and_return(1.year.ago)
|
||||
index_documents([chat_channel_membership1, chat_channel_membership2])
|
||||
params = { size: 5, user_id: user.id }
|
||||
params = { size: 5, user_id: [user.id] }
|
||||
|
||||
chat_channel_membership_docs = described_class.search_documents(params: params)
|
||||
expect(chat_channel_membership_docs.count).to eq(2)
|
||||
|
|
@ -113,7 +113,7 @@ RSpec.describe Search::ChatChannelMembership, type: :service do
|
|||
|
||||
it "will return a set number of docs based on pagination params" do
|
||||
index_documents([chat_channel_membership1, chat_channel_membership2])
|
||||
params = { page: 0, per_page: 1, user_id: user.id }
|
||||
params = { page: 0, per_page: 1, user_id: [user.id] }
|
||||
|
||||
chat_channel_membership_docs = described_class.search_documents(params: params)
|
||||
expect(chat_channel_membership_docs.count).to eq(1)
|
||||
|
|
@ -123,12 +123,12 @@ RSpec.describe Search::ChatChannelMembership, type: :service do
|
|||
allow(chat_channel_membership1).to receive(:channel_last_message_at).and_return(Time.current)
|
||||
allow(chat_channel_membership2).to receive(:channel_last_message_at).and_return(1.year.ago)
|
||||
index_documents([chat_channel_membership1, chat_channel_membership2])
|
||||
first_page_params = { page: 0, per_page: 1, sort_by: "channel_last_message_at", order: "dsc", user_id: user.id }
|
||||
first_page_params = { page: 0, per_page: 1, sort_by: "channel_last_message_at", order: "dsc", user_id: [user.id] }
|
||||
|
||||
chat_channel_membership_docs = described_class.search_documents(params: first_page_params)
|
||||
expect(chat_channel_membership_docs.first["id"]).to eq(chat_channel_membership1.id)
|
||||
|
||||
second_page_params = { page: 1, per_page: 1, sort_by: "channel_last_message_at", order: "dsc", user_id: user.id }
|
||||
second_page_params = { page: 1, per_page: 1, sort_by: "channel_last_message_at", order: "dsc", user_id: [user.id] }
|
||||
|
||||
chat_channel_membership_docs = described_class.search_documents(params: second_page_params)
|
||||
expect(chat_channel_membership_docs.first["id"]).to eq(chat_channel_membership2.id)
|
||||
|
|
@ -138,7 +138,7 @@ RSpec.describe Search::ChatChannelMembership, type: :service do
|
|||
allow(chat_channel_membership1).to receive(:channel_last_message_at).and_return(Time.current)
|
||||
allow(chat_channel_membership2).to receive(:channel_last_message_at).and_return(1.year.ago)
|
||||
index_documents([chat_channel_membership1, chat_channel_membership2])
|
||||
params = { page: 3, per_page: 1, user_id: user.id }
|
||||
params = { page: 3, per_page: 1, user_id: [user.id] }
|
||||
|
||||
chat_channel_membership_docs = described_class.search_documents(params: params)
|
||||
expect(chat_channel_membership_docs).to eq([])
|
||||
|
|
|
|||
|
|
@ -21,8 +21,8 @@ RSpec.describe Search::QueryBuilders::ChatChannelMembership, type: :service do
|
|||
expected_filters = [
|
||||
{ "term" => { "channel_status" => "active" } },
|
||||
{ "term" => { "channel_type" => "direct" } },
|
||||
{ "term" => { "status" => "active" } },
|
||||
{ "term" => { "viewable_by" => 1 } },
|
||||
{ "terms" => { "status" => %w[active joining_request] } },
|
||||
{ "terms" => { "viewable_by" => 1 } },
|
||||
]
|
||||
expect(filter.as_hash.dig("query", "bool", "filter")).to match_array(expected_filters)
|
||||
end
|
||||
|
|
@ -46,7 +46,7 @@ RSpec.describe Search::QueryBuilders::ChatChannelMembership, type: :service do
|
|||
"query" => "a_name*", "fields" => [:channel_text], "lenient" => true, "analyze_wildcard" => true
|
||||
}
|
||||
}]
|
||||
expected_filters = [{ "term" => { "channel_status" => "active" } }, { "term" => { "viewable_by" => 1 } }, { "term" => { "status" => "active" } }]
|
||||
expected_filters = [{ "term" => { "channel_status" => "active" } }, { "terms" => { "status" => %w[active joining_request] } }, { "terms" => { "viewable_by" => 1 } }]
|
||||
expect(query.as_hash.dig("query", "bool", "must")).to match_array(expected_query)
|
||||
expect(query.as_hash.dig("query", "bool", "filter")).to match_array(expected_filters)
|
||||
end
|
||||
|
|
@ -55,8 +55,18 @@ RSpec.describe Search::QueryBuilders::ChatChannelMembership, type: :service do
|
|||
params = { user_id: 1 }
|
||||
filter = described_class.new(params: params)
|
||||
expected_filters = [
|
||||
{ "term" => { "status" => "active" } },
|
||||
{ "term" => { "viewable_by" => 1 } },
|
||||
{ "terms" => { "status" => %w[active joining_request] } },
|
||||
{ "terms" => { "viewable_by" => 1 } },
|
||||
]
|
||||
expect(filter.as_hash.dig("query", "bool", "filter")).to match_array(expected_filters)
|
||||
end
|
||||
|
||||
it "always applies channel discoverable and status params" do
|
||||
params = { user_id: SiteConfig.mascot_user_id }
|
||||
filter = described_class.new(params: params)
|
||||
expected_filters = [
|
||||
{ "terms" => { "status" => %w[active joining_request] } },
|
||||
{ "terms" => { "viewable_by" => 1 } },
|
||||
]
|
||||
expect(filter.as_hash.dig("query", "bool", "filter")).to match_array(expected_filters)
|
||||
end
|
||||
|
|
@ -65,8 +75,8 @@ RSpec.describe Search::QueryBuilders::ChatChannelMembership, type: :service do
|
|||
params = { not_supported: "direct", user_id: 1 }
|
||||
filter = described_class.new(params: params)
|
||||
expected_filters = [
|
||||
{ "term" => { "status" => "active" } },
|
||||
{ "term" => { "viewable_by" => 1 } },
|
||||
{ "terms" => { "status" => %w[active joining_request] } },
|
||||
{ "terms" => { "viewable_by" => 1 } },
|
||||
]
|
||||
expect(filter.as_hash.dig("query", "bool", "filter")).to match_array(expected_filters)
|
||||
end
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue