Skip to content

josephwyang/accord

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Accord

Accord, a clone of Discord, is an instant messaging application where users can send each other direct messages and join and create servers to build Accord communities. This application uses a Ruby on Rails backend with a React frontend.

splash

Versions

  • ruby 2.5.1
  • rails 5.2.6
  • node v16.4.2
  • npm 7.18.1

Technologies and APIs

  • react/redux
  • postgresql
  • action cable
  • twilio

Features

Live Chat

This feature allows users to interact with messages real time, such as sending messages, editing them, deleting them, replying to them or reacting to them.

live-chat-demo.mov

Phone Number Verification

This feature uses the Twilio API to send text messages allowing for real phone number verification.

phone-number-verification-demo.mov

Public Servers

This feature allows users to create public server that can be discovered and joined by other users.

public-servers-demo.mov

Server Invitations / Notifications

This feature allows users to invite friends to servers real time.

server-invite-demo.mov

Message Settings

This feature was implemented to optimize readability of code while also allowing the component to be dynamic. This implementation takes advantage of the React-Redux framework and its single source of truth, allowing the component to access the currently logged in user.

import React from "react";
import Bubble from "../misc/bubble";

const MessageSettings = props => {
  const handleReply = () => {
    props.reply();
    document.querySelector("#message-form > span").focus();
  }
  return (
    <div className="message-settings" style={props.style}>
      <div>
        <img src={window.reaction} alt="reaction" onClick={e => props.react(e)} />
        <Bubble text="Add Reaction" top="-38px" />
      </div>
      {props.message.senderId === props.currentUserId && !props.message.invitation ?
        <div>
          <img src={window.edit} alt="edit" onClick={() => props.edit()}/>
          <Bubble text="Edit" top="-38px" />
        </div>
        : null}
      <div>
        <img src={window.reply} alt="reply" onClick={handleReply}/>
        <Bubble text="Reply" top="-38px" />
      </div>
      {props.message.senderId === props.currentUserId ?
        <div>
          <img src={window.trash} alt="delete" onClick={() => props.setDeleting()}/>
          <Bubble text="Delete" top="-38px" />
        </div>
        : null}
    </div>
  );
};

export default MessageSettings;

message-settings-current-user message-settings-other-user

Context Menu

This feature was implemented so that it can dynamically adapt to the whatever functionality is needed. The hardest part about this implentation was figuring out how to most effectively make the menu dynamic. It was done by passing in options with corresponding functions to list directly as elements in the context menu.

// simplified for demonstration purposes
import React, { useState, useEffect, useRef } from "react";

const ContextMenu = ({ options, top, left, closeMenu }) => {
  const [x, setX] = useState(0);
  const [y, setY] = useState(0);
  const containerRef = useRef(null);

  // closes context menu on outside click
  useEffect(() => {
    const handleOutsideClick = e => {
      if (containerRef.current && !containerRef.current.contains(e.target)) closeMenu();
    };
    
    document.addEventListener("mousedown", handleOutsideClick);
    return () => document.removeEventListener("mousedown", handleOutsideClick);
  }, []);
  
  // positions context menu within application dimensions
  const resize = () => {
    const menuDims = document.getElementById("context-menu").getBoundingClientRect();
    setX(Math.min(Math.max(800, window.innerWidth) - menuDims.width - 10, left));
    setY(Math.min(window.innerHeight - menuDims.height - 10, top));
  };
  
  useEffect(() => resize(), [options]);
  
  // maps menu items dynamically
  const menu = options.map((option, i) => (
    <li key={`option-${i}`} className={ option.disabled ? "disabled" : option.color }
    onClick={() => {
      option.function();
      closeMenu();
    }}>{option.text}</li>
  ));

  return (
    <ul id="context-menu" ref={containerRef} style={{ top: `${y}px`, left: `${x}px` }}>
      {menu}
    </ul>
  );
};

export default ContextMenu;

server-context-menu preview-context-menu member-context-menu channel-context-menu dm-context-menu


Future Prospects

Online/Offline Functionality

  • through Action Cable from Ruby on Rails

Voice Chat

  • through the Twilio API

About

discord clone; instant messaging application

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published