Chat

Allows current site visitors to chat with each other
Hello ~73.239.16.216 about 2 hours ago
Hello ~73.239.16.216 about 2 hours ago
The U of MN Gophers ๐Ÿ’ menโ€™s hockey team is destroying the MSU Spartans. ๐Ÿฅ… ~73.94.132.7 33 minutes ago

Code (106 LOC)

ERB (40 LOC)
app/views/chats/_demo.html.erb (16 LOC)
<ul class="nav nav-tabs">
<li class="nav-item">
<a data-toggle="tab" data-reflex="click->ChatReflex#set_color" href="#red" class="nav-link <%= "active" if active_chat?(:red) %>">Red Chat</a>
</li>
<li class="nav-item">
<a data-toggle="tab" data-reflex="click->ChatReflex#set_color" href="#yellow" class="nav-link <%= "active" if active_chat?(:yellow) %>">Yellow Chat</a>
</li>
<li class="nav-item">
<a data-toggle="tab" data-reflex="click->ChatReflex#set_color" href="#blue" class="nav-link <%= "active" if active_chat?(:blue) %>">Blue Chat</a>
</li>
</ul>
<div class="tab-content">
<%= render "color", color: "red" %>
<%= render "color", color: "yellow" %>
<%= render "color", color: "blue" %>
</div>
app/views/chats/_color.html.erb (13 LOC)
<div id="<%= color %>" data-color="<%= color %>"
data-controller="chat" data-action="chats:added@document->chat#scroll"
class="<%= css "tab-pane bg-white p-4 border border-top-0", "show active": active_chat?(color) %>">
<div data-target="chat.list" class="bg-light border rounded mb-2 p-3" style="height:400px; max-width:800px; overflow-y:scroll;">
<%= render partial: "chats/chat", collection: @chats.select { |chat| chat[:color] == color }, locals: { color: color } %>
</div>
<form style="max-width:800px;">
<div class="form-group">
<textarea data-target="chat.input" class="form-control border <%= chat_border_css color %>" rows="3" placeholder="Type your message..."></textarea>
</div>
<button data-action="click->chat#post" type="submit" class="btn btn-primary btn-lg">Submit</button>
</form>
</div>
app/views/chats/_chat.html.erb (11 LOC)
<% if chat_author? chat %>
<div class="<%= chat_css color %> rounded-lg border my-2 p-1 px-2 text-right w-75 float-right">
<%= chat[:message] %>
<small class="opacity-30 d-block">~me <%= distance_of_time_in_words_to_now Time.parse(chat[:created_at]) %> ago</small>
</div>
<% else %>
<div class="bg-light rounded-lg border my-2 p-1 px-2 text-left w-75 float-left">
<%= chat[:message] %>
<small class="opacity-30 d-block">~<%= chat[:author] %> <%= distance_of_time_in_words_to_now Time.parse(chat[:created_at]) %> ago</small>
</div>
<% end %>
JavaScript (30 LOC)
app/javascript/controllers/chat_controller.js (23 LOC)
import Rails from '@rails/ujs'
import ApplicationController from './application_controller'
export default class extends ApplicationController {
static targets = ['list', 'input']
connect () {
super.connect()
this.scroll(100)
}
post (event) {
Rails.stopEverything(event)
this.stimulate(
'ChatReflex#post',
this.element.dataset.color,
this.inputTarget.value
)
}
scroll (delay = 10) {
const lists = document.querySelectorAll('[data-target="chat.list"]')
setTimeout(() => {
lists.forEach(e => (e.scrollTop = e.scrollHeight))
}, delay)
}
}
app/javascript/channels/chat_channel.js (7 LOC)
import consumer from './consumer'
import CableReady from 'cable_ready'
consumer.subscriptions.create('ChatChannel', {
received (data) {
if (data.cableReady) CableReady.perform(data.operations)
}
})
Ruby (36 LOC)
app/controllers/chats_controller.rb (8 LOC)
class ChatsController < ApplicationController
def show
session[:chat_color] ||= "red"
@chats = Rails.cache.read(:chats) || []
@chats.shift while @chats.size > 50
Rails.cache.write :chats, @chats
end
end
app/reflexes/chat_reflex.rb (28 LOC)
class ChatReflex < ApplicationReflex
include ActionView::Helpers::SanitizeHelper
def post(color, message)
morph :nothing
message = strip_tags(message[0, 100])
return if message.blank?
chat = {
color: color,
author: request.remote_ip,
message: message,
created_at: Time.current.iso8601
}
chat_html = ChatsController.render(partial: "chats/chat", locals: {chat: chat, color: color})
list_selector = "##{color} [data-target='chat.list']"
input_selector = "##{color} textarea"
cable_ready.set_value(selector: input_selector, value: "", focus_selector: input_selector).broadcast
cable_ready["chat"]
.insert_adjacent_html(selector: list_selector, html: chat_html)
.dispatch_event(name: "chats:added")
.broadcast
chats = Rails.cache.read(:chats) || []
chats << chat
Rails.cache.write :chats, chats
end
def set_color
session[:chat_color] = element[:href].delete("#")
end
end