Giter Site home page Giter Site logo

quarrel's Introduction

Quarrel

Quarrel is a chat app modeled after Slack, but for casual discussions.

Languages/Tech

  • Ruby on Rails
  • JavaScript
  • React/Redux
  • React Router
  • Action Cable (to setup WebSocket connection)
  • Quill(rich text editor)
  • Emoji Mart

Selected features and code examples

User authentication implemented with Rails

I use a random number generator to create a session token and BCrypt to encrypt users' passwords. Users are verified by their email and password (checked against the password digest).

Example Code
  def self.find_by_credentials(email, password)
    user = User.find_by(email: email)
    return nil unless user && user.is_password?(password)
  end

  def password=(password)
      @password = password
      self.password_digest = BCrypt::Password.create(password)
  end

  def is_password?(password)
      BCrypt::Password.new(self.password_digest).is_password?(password)
  end

  def reset_token!
      self.session_token = SecureRandom.urlsafe_base64(16)
      self.save!
      self.session_token
  end

  private

  def ensure_session_token
      self.session_token ||= SecureRandom.urlsafe_base64(16)
  end

Channels list and search

Demo

For my channels list component, I use a local state variable to keep track of what users type. These search queries are then used to filter channels with JavaScript's built-in substrings function.

Example Code
this.state = {
  value: ''
};

getChannels() {
  return Object.values(this.props.channels).filter(x => x.channel_name.toLowerCase().search(this.state.value) != -1);
}
  
<AutoSizer>
  {({ height, width }) => (
    <List
      height={height}
      width={width}
      itemCount={searchChannels.length+1}
      itemSize={index => index > 0 ? 50 : 22}
      itemData={{ 
        channels: searchChannels.sort((a,b) => (a.channel_name.toLowerCase() < b.channel_name.toLowerCase()) ? -1 : 1),
        history: this.props.history,
        handleClose: this.handleCloseChannels,
        memberships: this.props.memberships
      }}
     >
       {Row}
     </List>
   )}
</AutoSizer>

Message component interprets media links (YouTube, jpg, png, gif)

Demo

The message component wraps a message based on its contents. Currently, saved messages are the main contents. I plan to make this more flexible with the input from the Quill text editor so that messages also support formatting and code blocks.

Example Code
const youtubeParser = url => {
  const regExp = /^.*((youtu.be\/)|(v\/)|(\/u\/\w\/)|(embed\/)|(watch\?))\??v?=?([^#\&\?]*).*/;
  const match = url.match(regExp);
  return (match && match[7].length == 11) ? match[7] : false;
}

function createMsgBody(msgBody) {
  const mediaExt = 'jpg jpeg png gif svg'.split(' ');
  let res = msgBody;
  if (validator.isURL(msgBody)) {
      let urlParts = msgBody.split('.');
      let ext = urlParts[urlParts.length - 1];
      if (mediaExt.includes(ext)) {
          res = (
              <img src={msgBody} style={{maxHeight: "360px", maxWidth: "360px"}}></img>
          );
      } else if (youtubeParser(msgBody)) {
          res = (
              <YouTube
                  videoId={youtubeParser(msgBody)}
                  opts={{
                      playerVars: {
                          autoplay: -1
                      }
                  }}
              />
          )
      } else {
          res = (<a href={msgBody} target="_blank">{msgBody}</a>)
      }
  }
  return res;
}

Realtime messaging with Action Cable (framework that integrates WebSockets with Rails)

Instant messaging is handled by Action Cable. This works by opening channels of communication, allowing my app to use a WebSocket connection.

Example Code
class Api::MessagesController < ApplicationController

  def create
      @message = current_user.messages.new(message_params) 
      @message.user = current_user
      if @message.save
          message_cable(@message)
          render json: @message
      else
          render json: @message.errors.full_messages, status: 422
     end
  end

  private

  def message_cable(message)
      ActionCable.server.broadcast(
          "messages#{message.channel_id}",
          id: message.id,
          body: message.body,
          user_id: message.user_id,
          channel_id: message.channel_id,
          created_at: message.created_at
      )
  end
end

class MessagesChannel < ApplicationCable::Channel
def subscribed
  stream_from "messages#{params[:channelId]}"
end
end

File Description
components Container and presentational components
actions These actions can be dispatched to trigger a Redux state change. They return POJOs that tell the reducer what the state should look like. Some of these actions also include async calls via middleware.
reducers Reducers specify how the state changes in response to dispatched actions.
util Defines functions that make API calls to Quarrel's backend.
controllers The API controllers define what happens given a requested route. The application controller defines helper methods for the backend.
models Maps database tables to Ruby objects, defines model level validations, specifies associations.
routes Declares URIs for the backend.

quarrel's People

Contributors

lance117 avatar dependabot[bot] avatar

Stargazers

Fion Pang  avatar

Watchers

Elliot avatar

quarrel's Issues

PA Review: User Auth

  • Backend: DB, model, controller, views
  • Redux Loop: ajax, actions, reducer
  • Presentational Components and Containers
  • Styling
  • Smooth, bug-free navigation
  • Adequate and appropriate seeds

Proposal Feedback

Wiki Page Home

  • Is the first page you see upon entering the wiki
  • Contains a welcome message
  • Contains a link/placeholder for a link to the live page
  • All links in the right sidebar should contain each wiki page and link to the correct page
  • Correctly formatted
    • each wiki page is listed in bullet points
    • all links route the correct page

Comments

Nice! Added some feedback. Please fix at your earliest convenience.

  • For live messaging, look into ActionCable! Here's a non-exhaustive list on the topic:

MVP List

  • Should have 7 MVPs.
    • 3 of those are User Auth, Heroku, and Production README.
    • The other 4 are from the MVP List or they have clarified them with you
  • Contains a description sentence of the app
  • Includes two to three detailed bullets on functionality and presentation of feature
  • At least one CRUD feature, which states what CRUD operations are planned (creation, reading, updating, deletion)
  • Estimates how long it will take the code each MVP
  • Correctly formatted
    • MVPs are listed in an ordered list
    • Each MVP is broken down into bullet points

Comments

  • You'll need a total of 7 MVPs. I'd recommend
    • Workspaces/Teams
    • Channels within Teams
    • Live Chat & Messages (Messages should be your CRUD feature: create, read, update, delete)
    • DMs

Database Schema

  • Contains correct datatypes
  • Contains appropriate constraints/details
    • primary key
    • not null
    • unique
    • indexed
    • foreign key
  • Contains bullet points after the table that state which foreign keys will reference to which table, or references to the associations which will be made
    • foreign key and table name are lowercased, snake_cased and back_ticked
  • Correctly formatted
    • schema is written in a table format
    • the table's name are lowercased, snake_cased and back_ticked
    • the table header column names are bolded
    • columns names are lowercased and snaked_cased and back_ticked

Comments

  • If you have workspaces (as per MVP feedback above), you'll need:
    • A workspaces table (with a name)
    • Update channels to have a workspace_id
    • A workspaces_users join table (so a user can be in many workspaces, and a workspace can have many users)

Sample State

  • State shape is flat!
  • State's keys are camelCased
  • All keys within the values in the state are accessible in the schema
  • Correctly formatted
    • Sample state is rendered with triple backticks, and the language ```javascript...```). This will display the state as a code block instead of a giant line of text
    • Top level slices
      • entities
      • session
      • errors (here or in ui)
      • ui (if needed)
    • Should NOT have nested slices, aka comments inside of posts
      • Some info from other tables is ok, for instance:
        • the author username and imageurl for a post. basically any info that the user can't change
        • like count and a boolean on whether the user likes the post instead of a likes slice

Comments

  • I wouldn't worry about the distinction between byId and allIds
  • I wouldn't have a joinedChannelIds array in users (unless the UI depends on ordering). You should have a channelMemberships slice of state that stores what users are in what channels. You can have a frontend selector to filter the channels a user belongs to, and have another selector to filter the member list of a channel. (See code snipped below for what I mean by this slice of state)
  • Similarly, I wouldn't have memberIds array in channels, as you should have a separate slice
  • You'll need a workspaces slice with that MVP, and likely an activeWorkSpace key in your ui slice of state.
  • You may need to store an activeChannel key in your ui slice of state to store what channel a user is currently looking at
// ... in your state 
channelMemberships: {
  2: {
    id: 2, 
    userId: 10,
    channelId: 3
  },
  3: {
    id: 3, 
    userId: 42, 
    channelId: 2
  }
}

Backend Routes

  • Contains the following sections: HTML, API Endpoints(Backend)
  • Each route has a description
  • API Endpoint routes contains wildcard variables written in snake_case
  • Routes does not contain superfluous routes
  • Have API routes that will allow the front end to get all info it needs and does not have unneeded routes:
    • probably doesn't need a GET likes api endpoint because that info comes through the post show

Comments

  • Missing from proposal

Frontend Routes

  • Frontend routes contains wildcard variables written in camelCase
  • Correctly formatted
    • Routes are displayed with inline coding text (backticks)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.