I'm working on a basic private messaging system for my application. I started with this tutorial on Medium.
I discovered an issue though. It returns all conversations, not just the ones that the current user is actually involved in. The view only shows the ones you're in but all the records are there. Obviously that is horrible if you have more than a handful of users.
I've tweaked the controller to what I thought was the solution but I'm still getting all records so I'm thinking the issue is in the model.
Conversations Controller
class ConversationsController < ApplicationController
before_action :authenticate_user!
before_action :set_conversation, only: [:destroy]
def index
@user = current_user
@conversations = Conversation.where(:sender_id == @user.id || :recipient_id == @user.id)
end
def create
if Conversation.between(params[:sender_id],params[:recipient_id])
.present?
@conversation = Conversation.between(params[:sender_id],
params[:recipient_id]).first
else
@conversation = Conversation.create!(conversation_params)
end
redirect_to conversation_messages_path(@conversation)
end
Conversation Model
class Conversation < ActiveRecord::Base
belongs_to :sender, :foreign_key => :sender_id, class_name: 'User'
belongs_to :recipient, :foreign_key => :recipient_id, class_name: 'User'
has_many :messages, dependent: :destroy
validates_uniqueness_of :sender_id, :scope => :recipient_id
scope :between, -> (sender_id,recipient_id) do
where(sender_id: [sender_id,recipient_id], recipient_id: [sender_id,recipient_id])
end
def unread_message_nr_for(user_id)
messages.where('messages.read = ?', false).where('messages.user_id != ?', user_id).count
end
end
Conversation View
<div class="ibox-content no-side-borders">
<% @conversations.each do |conversation| %>
<div class="conversation-member">
<% if conversation.sender_id == current_user.id || conversation.recipient_id == current_user.id %>
<% if conversation.sender_id == current_user.id %>
<% recipient = User.find(conversation.recipient_id) %>
<% else %>
<% recipient = User.find(conversation.sender_id) %>
<% end %>
<span class="<%= 'current-conversation' if (params['conversation_id'].to_i == conversation.id) %>">
<% if recipient.avatar.present? %>
<%= image_tag(recipient.avatar_url(:navigation), class: "img-circle m-r-xs") %>
<% end %>
<%= link_to recipient.first_name + " " + recipient.last_name, conversation_messages_path(conversation)%>
</span>
<% if conversation.unread_message_nr_for(current_user.id) > 0 %>
<span class="badge-inline">
<%= conversation.unread_message_nr_for(current_user.id) %>
</span>
<% end %>
<% end %>
</div>
<% end %>
</div>
Conversations Schema
create_table "conversations", force: :cascade do |t|
t.integer "sender_id"
t.integer "recipient_id"
t.datetime "created_at"
t.datetime "updated_at"
end
How can I get only the conversations where the current user is the sender_id or the recipient_id?