天亮了 昨晚是平安夜
洛神:2號玩家請繼續發言
5號:我其實第一輪第二輪都覺得2號蠻好的,因為有點分不清楚預言家給7號容忍度是好人會想的思考量,所以我覺得2號是好的,2號敢保8號我覺得應該蠻好的,9號當然是比7好飽滿很多,但是作為好人我還是有點分不出來,所以我比較懷疑10號,不確定他有沒有可能做成一張倒鉤是因為前面我是第一個點出6號這張牌,10號,再來就是馬上可以刀中神刀刀中神,感覺得3跟8號比較有可能,因為在我視角已經把2號放在偏好了,然後3號沒有打2打的那麼嚴重所以又有一點像好人,可能會想從8 10出,著重聽8 10
待續..
今天我們透過hotwire來整合聊天
首先建立 messages 的 controller model 跟 views
並把他跟房間做has many的關聯
/2021100416xxxx_create_messages.rb
class CreateMessages < ActiveRecord::Migration[6.1]
def change
create_table :messages do |t|
t.references :room, null: false, foreign_key: true
t.string :user_id
t.string :nickname
t.text :content
t.timestamps
end
end
end
把db整合進我們的資料庫
$ bundle exec rake db:migrate
class MessagesController < ApplicationController
before_action :set_room, only: %i[ new create ]
before_action :set_message, only: %i[ update edit destroy show ]
def new
@message = @room.messages.new
end
def create
@message = @room.messages.new(message_params)
@message.user_id = current_user.id
@message.nickname = current_user.name
if @message.save
render turbo_stream: turbo_stream.append(:messages, @message)
else
render 'new', layout: false, status: :unprocessable_entity
end
end
private
def set_room
@room = Room.find(params[:room_id])
end
def set_message
@message = Message.find(params[:id])
end
def message_params
params.require(:message).permit(:content, :room_id, :nickname, :user_id)
end
end
/rooms_controller
def show
assign_seat_to_user(@room)
@messages = @room.messages
.order(:created_at)
@new_message = Message.new(room: @room)
end
加上broadcasts讓hotwire內建的ActionCable
也就是要把訊息廣播到房間裡面
/message.rb
class Message < ApplicationRecord
belongs_to :room
broadcasts_to :room
validates :room, :content, presence: true
def edited?
created_at != updated_at
end
end
room這邊也需要加上broadcasts
並與messages是一對多的關聯
/room.rb
class Room < ApplicationRecord
resourcify
has_many :seats, dependent: :destroy
has_many :messages, dependent: :destroy
broadcasts
end
在room show page中加入訊息的顯示
/show.erb
<div id="messages">
<%= render @room.messages %>
</div>
<%= turbo_frame_tag "new_message", src: new_room_message_path(@room), target: "_top" %>
補上messages的view端,所需要的分別是訊息顯示partial與form表單
/views/messages/_message.html.erb
<%= turbo_frame_tag dom_id(message), class: 'pb-1 px-3',
data: { controller: 'message', action: 'mouseout->message#toggleActions mouseover->message#toggleActions', 'message-author-id-value': message.user_id } do %>
<div class='row row-cols-auto gx-2'>
<div class='col fw-bold'>
<%= message.nickname %>
</div>
<div class='col'>
<%= local_time message.created_at, format: :short, class: 'fw-light fs-7' %>
</div>
<% if message.edited? %>
<div class='col'>
<span class='fw-light fs-7'>edited <%= local_time message.updated_at, format: :short %></span>
</div>
<% end %>
</div>
<div class='formatted-content'>
<%= message.content %>
</div>
<% end %>
/views/messages/form.html.erb
<%= form_with model: message.persisted? ? message : [message.room, message], data: { controller: 'form', action: 'turbo:submit-end->form#resetForm' } do |f| %>
<%= f.text_field :content, class: 'form-control', required: true, autocomplete: 'off', autofocus: true, placeholder: "Message ##{message.room.name}" %>
<% if message.persisted? %>
<div class='pt-2'>
<%= link_to 'Cancel', @message, class: 'btn btn-sm btn-outline-secondary' %>
<%= f.submit 'Save changes', class: 'btn btn-sm btn-primary'%>
<div>
<% end %>
<% end %>
/views/messages/new.html.erb
<%= turbo_frame_tag @message do %>
<%= render 'messages/form', message: @message %>
<% end %>
在javascript下controller資料夾新增清空輸入框的js
/javascipts/controllers/reset_form_controller.js
import { Controller } from "stimulus"
export default class extends Controller {
reset() {
this.element.reset()
}
}
/views/messages/edit.html.erb
<%= turbo_frame_tag dom_id(@message) do %>
<div data-controller='scroll-into-view' class='py-2'>
<%= render 'form', message: @message %>
</div>
<% end %>
/views/messages/show.html.erb
<%= render @message %>
這樣我們就算是把hotwire的turbo_frame設定好了
再來我們研究一點前幾天沒碰到的turbo_stimulus
先在vite的 /javascripts/entrypoints/application.js 把檔案import進來
/javascripts/entrypoints/application.js
import Rails from '@rails/ujs'
import '@hotwired/turbo-rails'
import '../controllers'
import '../channels'
Rails.start()
在controllers的資料夾下
先處理message_controller.js
這樣我們就算是把hotwire的turbo_frame設定好了
再來我們研究一點前幾天沒碰到的turbo_stimulus
先在vite的 /javascripts/entrypoints 把檔案import進來
/javascripts/controllers/message_controller.js
import { Controller } from "@hotwired/stimulus"
import { currentUserId } from '../helpers/auth'
export default class extends Controller {
static targets = ['actions']
static values = {
userId: String,
}
connect() {
if (document.querySelectorAll(`#${this.element.id}`).length > 1) {
this.element.remove()
return
}
this.element.scrollIntoView({ block: 'nearest' })
}
toggleActions() {
if (this.hasActionsTarget && this.authorIdValue === currentUserId()) {
this.actionsTarget.classList.toggle('invisible')
}
}
}
/javascripts/controllers/message_list_controller.js
import { Controller } from "@hotwired/stimulus"
export default class extends Controller {
connect() {
this.scrollToBottom()
}
scrollToBottom() {
this.element.scrollTop = this.element.scrollHeight
}
}
/javascripts/controllers/scroll_into_view_controller.js
import { Controller } from "@hotwired/stimulus"
export default class extends Controller {
connect() {
this.element.scrollIntoView({ block: 'nearest' })
}
}
在這邊設定訊息不能空白
/javascripts/controllers/form_controller.js
import { Controller } from "@hotwired/stimulus"
const ERROR_BLANK = "Can't be blank"
const validatePresence = (input) => {
if (input.hasAttribute('required')) {
if (input.value.trim() === '') {
input.setCustomValidity(ERROR_BLANK)
return false
} else {
if (input.validity.customError) input.setCustomValidity('')
return true
}
} else {
return true
}
}
export default class extends Controller {
connect() {
this.element.querySelectorAll('input[required]').forEach((input) => {
input.addEventListener('input', this.validateInput)
})
}
disconnect() {
this.element.querySelectorAll('input[required]').forEach((input) => {
input.removeEventListener('input', this.validateInput)
})
}
validateInput() {
validatePresence(this) // && validateSomethingElse()
}
resetForm() {
this.element.reset()
}
}
/javascripts/helpers/auth.js
const DATA_ATTR_NAME = 'data-current-user-id'
export const currentUserId = () => {
return (
document
.querySelector(`[${DATA_ATTR_NAME}]`)
?.getAttribute(DATA_ATTR_NAME) || null
)
}
最後再route設定
/routes.rb
resources :rooms do
resources :messages
end
這麼一來訊息就可以出現在房間內囉
下面這幾個是特別找的 hotwire 完整專案 我覺得現在資料蠻散的
推薦剛接觸hotwire的朋友可以先從這幾個完整專案開始接觸唷
reference:
$loom 可以等過 $0.118
天黑請閉眼