Skip to content

Commit

Permalink
feat: add chat logic, add chat members logic
Browse files Browse the repository at this point in the history
  • Loading branch information
kkkiikkk committed Sep 18, 2024
1 parent 1a01241 commit 937adb4
Show file tree
Hide file tree
Showing 26 changed files with 413 additions and 7 deletions.
1 change: 1 addition & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,4 @@ end

gem "devise"
gem "listen", "~> 3.9"
gem 'pundit'

Check failure on line 55 in Gemfile

View workflow job for this annotation

GitHub Actions / lint

Style/StringLiterals: Prefer double-quoted strings unless you need single quotes to avoid extra backslashes for escaping.
3 changes: 3 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,8 @@ GEM
stringio
puma (6.4.2)
nio4r (~> 2.0)
pundit (2.4.0)
activesupport (>= 3.0.0)
racc (1.8.1)
rack (3.1.7)
rack-session (2.0.0)
Expand Down Expand Up @@ -307,6 +309,7 @@ DEPENDENCIES
listen (~> 3.9)
pg (~> 1.1)
puma (>= 5.0)
pundit
rails (~> 7.2.1)
rubocop-rails-omakase
sprockets-rails
Expand Down
2 changes: 2 additions & 0 deletions app/controllers/application_controller.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
class ApplicationController < ActionController::Base
include Pundit

Check failure on line 3 in app/controllers/application_controller.rb

View workflow job for this annotation

GitHub Actions / lint

Layout/TrailingWhitespace: Trailing whitespace detected.
allow_browser versions: :modern

before_action :configure_permitted_parameters, if: :devise_controller?
Expand Down
58 changes: 58 additions & 0 deletions app/controllers/chat_members_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
class ChatMembersController < ApplicationController
before_action :authenticate_user!
before_action :set_workspace
before_action :set_chat
before_action :set_chat_member, only: [:destroy, :index, :new, :create]

Check failure on line 5 in app/controllers/chat_members_controller.rb

View workflow job for this annotation

GitHub Actions / lint

Layout/SpaceInsideArrayLiteralBrackets: Use space inside array brackets.

Check failure on line 5 in app/controllers/chat_members_controller.rb

View workflow job for this annotation

GitHub Actions / lint

Layout/SpaceInsideArrayLiteralBrackets: Use space inside array brackets.
before_action :require_admin_or_owner!, only: [:new, :create]

Check failure on line 6 in app/controllers/chat_members_controller.rb

View workflow job for this annotation

GitHub Actions / lint

Layout/SpaceInsideArrayLiteralBrackets: Use space inside array brackets.

Check failure on line 6 in app/controllers/chat_members_controller.rb

View workflow job for this annotation

GitHub Actions / lint

Layout/SpaceInsideArrayLiteralBrackets: Use space inside array brackets.

def index
@chat_members = @chat.chat_members
end

def new
@chat_member = @chat.chat_members.new
end

def create
@chat_member = @chat.chat_members.new(chat_member_params)
@chat_member.role = 'member'
@chat_member.chat = @chat
if @chat_member.save
redirect_to workspace_path, notice: 'User was succesfully added to the chat'
else
render :new, notice: 'Failed creation. Try again'
end
end

def destroy
@chat_member.destroy
redirect_to workspaces_path, notice: 'User were succesfully removed from the chat'
end

private

def set_workspace
@workspace = current_user.workspaces.find_by(id: params[:workspace_id])
unless @workspace
redirect_to workspaces_path, notice: 'Workspace not found or not accessible.'
end
end

def set_chat
@chat = @workspace.chats.find(params[:chat_id])
end

def set_chat_member
@chat_member = @chat.chat_members.find_by(user_id: current_user.id)
end

def require_admin_or_owner!
if ChatMemberPolicy.new(current_user, @chat_member).member?
redirect_to workspaces_path, notice: 'You can not manage chat members because you have no access'
end
end

def chat_member_params
params.require(:chat_member).permit(:user_id)
end
end
55 changes: 55 additions & 0 deletions app/controllers/chats_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
class ChatsController < ApplicationController
before_action :authenticate_user!
before_action :set_workspace
before_action :require_workspace_owner!, only: [:new, :create, :destroy]

def index
@chats = ChatsQuery.new(@workspace, current_user).all_chats
end

def show
@chat = @workspace.chats.find(params[:id])
end

def new
@chat = @workspace.chats.new
end

def create
result = ChatsService::Create.call(current_user, chat_params, @workspace)
if result.success?
redirect_to workspace_chats_path(@workspace), notice: result[:payload][:text]
else
@chat = @workspace.chats.new(chat_params)
render :new
end
end



def destroy
@chat = @workspace.chats.find(params[:id])
@chat.destroy
redirect_to workspace_chats_path(@workspace), notice: 'Chat was successfully deleted.'
end

private

def set_workspace
@workspace = current_user.workspaces.find_by(id: params[:workspace_id])

unless @workspace
redirect_to workspaces_path, alert: 'Workspace not found or not accessible.'
end
end

def chat_params
params.require(:chat).permit(:name, :chat_type, :companion_id)
end

def require_workspace_owner!
if !WorkspacePolicy.new(current_user, @workspace).owner?
redirect_to workspaces_path, notice: 'You can not manage the chat because you are not workspace owner'
end
end
end
2 changes: 2 additions & 0 deletions app/helpers/chat_members_helper.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
module ChatMembersHelper
end
2 changes: 2 additions & 0 deletions app/helpers/chats_helper.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
module ChatsHelper
end
1 change: 1 addition & 0 deletions app/javascript/application.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// Configure your import map in config/importmap.rb. Read more: https://github.com/rails/importmap-rails
import "@hotwired/turbo-rails"
import "controllers"

12 changes: 12 additions & 0 deletions app/models/chat.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
class Chat < ApplicationRecord
belongs_to :workspace
has_many :chat_members

validates :name, presence: true
validates :chat_type, inclusion: { in: %w[public private p2p], message: "%{value} is not a valid type" }
validates :chat_status, inclusion: { in: %w[active archive], message: "%{value} is not a valid type" }

def private_or_p2p?
chat_type.in?(%w[private p2p])
end
end
15 changes: 15 additions & 0 deletions app/models/chat_member.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
class ChatMember < ApplicationRecord
belongs_to :chat
belongs_to :user

validates :role, inclusion: { in: %w[member owner admin], message: "%{value} is not a valid role" }
validate :chat_must_be_private_or_p2p

private

def chat_must_be_private_or_p2p
unless chat.chat_type.in?(%w[private p2p])
errors.add(:chat, 'must be private or p2p')
end
end
end
1 change: 1 addition & 0 deletions app/models/workspace.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ class Workspace < ApplicationRecord
has_many :user_workspaces, dependent: :destroy
has_many :users, through: :user_workspaces
has_many :invites
has_many :chats
belongs_to :user

has_one_attached :picture
Expand Down
21 changes: 21 additions & 0 deletions app/policies/chat_member_policy.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
class ChatMemberPolicy < ApplicationPolicy
def member?
record.role == 'member'
end

def admin?
record.role == 'admin'
end

def owner?
record.role == 'owner'
end

def can_remove?
['owner', 'admin'].include?(record.role)
end

def can_add_members?
['owner', 'admin'].include?(record.role)
end
end
4 changes: 4 additions & 0 deletions app/policies/workspace_policy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,8 @@ class WorkspacePolicy < ApplicationPolicy
def can_destroy?
record_owner?
end

def owner?
record_owner?
end
end
41 changes: 41 additions & 0 deletions app/queries/chats_query.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
class ChatsQuery
def initialize(workspace, user)
@workspace = workspace
@user = user
end

def all_chats
Chat.find_by_sql([combined_chats_sql])
end

private

def combined_chats_sql
<<-SQL
(#{public_chats_sql.to_sql})
UNION
(#{private_chats_sql.to_sql})
UNION
(#{p2p_chats_sql.to_sql})
SQL
end

def public_chats_sql
Chat.select('*')
.where(workspace_id: @workspace.id, chat_type: 'public', chat_status: 'active')
end

def private_chats_sql
Chat.select('chats.*')
.joins(:chat_members)
.where(workspace_id: @workspace.id, chat_type: 'private', chat_status: 'active')
.where(chat_members: { user_id: @user.id })
end

def p2p_chats_sql
Chat.select('chats.*')
.joins(:chat_members)
.where(workspace_id: @workspace.id, chat_type: 'p2p', chat_status: 'active')
.where(chat_members: { user_id: @user.id })
end
end
48 changes: 48 additions & 0 deletions app/services/chats_service/create.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
module ChatsService
class Create < ApplicationService
def call(current_user, params, workspace)
@user = current_user
@params = params
@chat = Chat.new(chat_type: @params[:chat_type], chat_status: 'active', name: @params[:name], workspace: workspace)

if @params[:chat_type] == 'p2p'
create_p2p_chat
else
create_general_chat
end
end

private

def create_p2p_chat
if @chat.save
ChatMember.create!(user: @user, chat: @chat, role: 'owner')
ChatMember.create!(user_id: @params[:companion_id], chat: @chat, role: 'owner')
success({ text: "Chat was successfully created" })
else
log_chat_errors
failure({ text: "Chat can't be created. Please try again later." })
end
end

def create_general_chat
if @chat.save
puts "HELLO"
new_chat_member = ChatMember.new(user: @user, chat: @chat, role: 'owner')
if new_chat_member.save
success({ text: "Chat was successfully created" })
else
Rails.logger.error "Chat creation failed: #{new_chat_member.errors.full_messages.join(', ')}"
failure({ text: "Chat can't be created. Please try again later." })
end
else
log_chat_errors
failure({ text: "Chat can't be created. Please try again later." })
end
end

def log_chat_errors
Rails.logger.error "Chat creation failed: #{@chat.errors.full_messages.join(', ')}"
end
end
end
14 changes: 14 additions & 0 deletions app/views/chat_members/index.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<h1>Members in Chat: <%= @chat.name %></h1>

<ul>
<% @chat_members.each do |member| %>
<li>
<%= member.user.name %> - <%= member.role.capitalize %>
<%= link_to 'Remove', workspace_chat_chat_member_path(@workspace, @chat, member), method: :delete, data: { confirm: 'Are you sure?', turbo_method: :delete }, class: 'btn btn-danger' %>
</li>
<% end %>
</ul>

<%= link_to 'Add Member', new_workspace_chat_chat_member_path(@workspace, @chat), class: 'btn btn-primary' %>

<%= link_to 'Back to Chat', workspace_chat_path(@workspace, @chat) %>
12 changes: 12 additions & 0 deletions app/views/chat_members/new.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<h1>Add Member to <%= @chat.name %></h1>

<%= form_for [@workspace, @chat, @chat_member] do |f| %>
<div class="form-group">
<%= f.label :user_id, 'User ID' %>
<%= f.number_field :user_id, class: 'form-control' %>
</div>

<%= f.submit 'Add Member', class: 'btn btn-primary' %>
<% end %>

<%= link_to 'Back to Chat', workspace_chat_path(@workspace, @chat) %>
12 changes: 12 additions & 0 deletions app/views/chats/index.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<h1>Chats in <%= @workspace.name %></h1>

<%= link_to 'Create New Chat', new_workspace_chat_path(@workspace), class: 'btn btn-primary' if policy(@workspace).owner? %>

<ul>
<% @chats.each do |chat| %>
<li>
<%= link_to chat.name, workspace_chat_path(@workspace, chat) %>
<%= "(#{chat.chat_type.capitalize})" %>
</li>
<% end %>
</ul>
33 changes: 33 additions & 0 deletions app/views/chats/new.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<%= form_for [@workspace, @chat] do |f| %>
<div class="form-group">
<%= f.label :name %>
<%= f.text_field :name, class: 'form-control' %>
</div>

<div class="form-group">
<%= f.label :chat_type %>
<%= f.select :chat_type, options_for_select([['Public', 'public'], ['Private', 'private'], ['P2P', 'p2p']]), {}, class: 'form-control' %>
</div>

<div class="form-group" id="companion-field" style="display: none;">
<%= label_tag :companion_id, 'Companion User ID' %>
<%= text_field_tag :companion_id, nil, class: 'form-control' %>
</div>

<%= f.submit 'Create Chat', class: 'btn btn-primary' %>
<% end %>

<script>
document.addEventListener('turbolinks:load', function() {
const chatTypeField = document.querySelector('select[name="chat[chat_type]"]');
const companionField = document.getElementById('companion-field');

chatTypeField.addEventListener('change', function() {
if (chatTypeField.value === 'p2p') {
companionField.style.display = 'block';
} else {
companionField.style.display = 'none';
}
});
});
</script>
Loading

0 comments on commit 937adb4

Please sign in to comment.