Skip to content

Lawrenccee/TeaMi

Repository files navigation

TeaMí

Live Demo

TeaMí was inspired by Facebook Messenger, which is a real-time messenger. TeaMí is able to deliver instant messaging through technologies like Rails and ActionCable, PostgreSQL, React.js, Redux, and AWS S3 storage along with paperclip. This project was given a timeframe of 10 days. Knowing this, features are limited, but I plan on expanding and making the application better overall.

Features

  • User Authentication, backend and frontend, utilizaing BCrypt
  • Creating Chats with any other user that has an account
  • Messages or sent in real-time, thanks to Rails' ActionCable, a helper for websockets
  • Sending Gifs thanks to the Giphy API
  • Adding more members to a chat
  • Changing your profile picture, chat picture, or chat name

Messages

When creating a new user account, the default new chat page is shown. Users can be searched through the search bar of the chat.

If there already exists a chat with that user or group then that specific chat will be opened instead.

This is done with a query of the chats that the current user belongs to. Then by going through each chats members to see if this specific group of users already exists. If they do then the existing chat is sent back and opened. It is done like so:

...
@ids = []

@user_chats = current_user.chats.includes(:members)
    @user_chats.find_each do |chat|
      @member_ids = chat.members.pluck(:id)

      if (@member_ids.size == @ids.size) && ((@member_ids & @ids) == @member_ids)
        @exists = true
        @chat = chat
        break
      end
    end

    if @exists 
      render :show
...

An includes is used to fetch the data for all the members of the chats when the first query to sets @user_chats is done. So when it loops through all of the chats their members already exist, preventing another query, and overall an N+1 query.

The most important bit is the real-time messaging. By using ActionCable in Rails, websockets are implemented to have users listening for new messages in their current chats as well as their own person channel to see if they have a new chat coming.

This is done by subscribing the user to certain channels to listen for messages which are then broadcast to other users on that channel.

Frontend:

setUpChat(chatId, receiveData, userId) {
    App.chat = App.cable.subscriptions.create({ channel: "ChatChannel", chat_id: chatId, user_id: userId }, {
      connected: function () {
        setTimeout(this.perform('subscribed'), 1000);
      },

      received: function (data) {
        receiveData();
      },

      speak: function (message) {
        return this.perform('speak', message);
      }
    });
  }

Backend (to save the message to database and broadcast the message):

class ChatChannel < ApplicationCable::Channel
  def subscribed
    if params[:chat_id]
      stream_from "chats-#{params[:chat_id]}"
    end

    if params[:user_id]
      stream_from "users-#{params[:user_id]}"
    end      
  end

  def unsubscribed
    stop_all_streams
  end

  def speak(data)
    message_params = {
      body: data["body"],
      giphy_url: data["giphy_url"],
      chat_id: data["chat_id"],
      author_id: data["author_id"]
    }

    message = Message.create(message_params)
  end
end

...

class MessageBroadcastJob < ApplicationJob
  queue_as :default

  def perform(message)
    broadcast_to_chat(message)
  end

  def broadcast_to_chat(message)
    message.chat.member_ids.each do |user_id|
      ActionCable.server.broadcast(
        "users-#{user_id}",
        message: JSON.parse(render_message(message))
      )
    end

    ActionCable.server.broadcast(
      "chats-#{message[:chat_id]}",
      message: JSON.parse(render_message(message))
    )
  end

  def render_message(message)
    ApplicationController.render(
      partial: 'api/messages/message',
      locals: { message: message}
    )
  end
end

Users are subscribed to channels on the frontend, which then connects them to certain channels that they are listening to. When a message is sent it is saved to the database on the backend then broadcast to certain channels which calls the received method on the frontend. The user can then update their data depending on what they received. Keep in mind that messages are only received if you are subscribed to the channel that the message was sent to.

Chat

Users can click on different chats to open up the messages in the chat. Users can search through their chats dynamically as shown:

This is done with another database query, utilizing the power of ActiveRecord in Rails.

...
if current_user
      if params[:query].length > 0
        @chats = current_user.chats.where("LOWER(name) ILIKE ?", "%#{params[:query]}%")
      else 
        @chats = current_user.chats
      end
    else
      render json: {}
    end
...

The query fetches whatever chat names that the user has that match the query string pattern.

ChatInfo

Through the ChatInfo bar users can add members to the chat as well as change the chat photo as well as the chat name.

It is also worth noting that by hovering over your own profile picture in the top left that you can also change your personal profile picture.

This is done with aws and the paperclip gem.

Design

TeaMí was designed with trying to make a close, but different, clone of Messenger. I mostly tried to copy the main features of Messenger within the 10 day timeframe we had. Sometimes, features were a main priority over clean code. Now that the application works with basic functionalities, I plan on refactoring my code before moving on to make everything more clean and crisp. I hope that this will serve as a good reference to myself in the future.

Technologies

Rails was used because it's convention over configuration, so you can get a basic site up and running in a short amount of time. ActiveRecord and ActionCable served as great tools for my app to work.

Redux was used with React in order to have easier state management. It's much easier to have a global state object, so you can keep track of what slice of state is being passed around where.

AWS was used for image storage along with the paperclip gem to help with uploading and image validations.

Future Improvements

More things on my list to add to the application include:

  • Being able to edit the user's username
  • A chatbot
  • Load older messages
  • Notifications
  • Milk Tea styling
  • Making it mobile friendly

About

A Facebook Messenger Clone with a Milk Tea Twist

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published