al6 / diskord

Discord Clone
14 stars 8 forks source link

Live Site

Diskord

I'm a huge fan of the popular Discord App. Diskord is my take on cloning the real deal. Gamers (or anyone really) can sign up, login, create guilds (with optional guild emblems), create guild channels, and chat (with optionally attached images/gifs) with guild members all in real time.

Screenshot of splash page Gif of websockets working

Technologies used

Diskord was built using React, Redux, Ruby on Rails, ActionCable, Active Storage, PostgreSQL, and AWS S3

Features

Members

Action Cable Implementation

I wanted to keep my React presentational component as readable and simple as possible, so I chose to compose a function and pass it down with dispatch to that same presentational component. Code related to implementing action cable is shown below.

chat_channel.rb

messages_controller.rb (abridged from original)

channel_messages_index_container.js (abridged from original)

import { receiveMessage } from "../../actions/message_actions";

let subscription
function subscribeToChannel(channelId, dispatch) {
  if (subscription) {
    subscription = subscription.unsubscribe();
  }
  subscription = App.cable.subscriptions.create(
    { channel: "ChatChannel", channelId: channelId },
    {
      received: data => {
        dispatch(receiveMessage(data));
      }
    }
  );
}

const mapDispatchToProps = dispatch => {
  const subscribe = channelId => subscribeToChannel(channelId, dispatch);
  return {
    subscribe,
  };
};

channel_messages_index.jsx (abridged from original)

componentDidMount() {
    this.props.fetchMessages(this.props.channelId);
    if (Number.isInteger(this.props.channelId)) {
      this.props.subscribe(this.props.channelId);
    }
  }

componentDidUpdate(previousProps) {
  if (previousProps.channelId !== this.props.channelId) {
    this.props.fetchMessages(this.props.channelId);
    this.props.subscribe(this.props.channelId);
  }
  if (previousProps.messages.length !== this.props.messages.length) {
    this.messagesIndex.current.scrollIntoView();
  }
}