74 lines
2.6 KiB
JavaScript
74 lines
2.6 KiB
JavaScript
import { Controller } from "@hotwired/stimulus"
|
|
import { createConsumer } from "@rails/actioncable"
|
|
|
|
export default class extends Controller {
|
|
static values = {
|
|
boardId: Number,
|
|
currentParticipantId: Number
|
|
}
|
|
|
|
connect() {
|
|
this.channel = createConsumer().subscriptions.create(
|
|
{ channel: "BoardChannel", board_id: this.boardIdValue },
|
|
{
|
|
received: (data) => {
|
|
this._insertMessage(data)
|
|
}
|
|
}
|
|
)
|
|
this.element.scrollTop = this.element.scrollHeight
|
|
}
|
|
|
|
disconnect() {
|
|
if (this.channel) {
|
|
this.channel.unsubscribe()
|
|
}
|
|
}
|
|
|
|
_insertMessage(data) {
|
|
const isMe = data.participant_id === this.currentParticipantIdValue
|
|
|
|
// 最初の投稿の場合、"まだ投稿がありません"メッセージを消す
|
|
const noPostsMessage = document.getElementById("no-posts-message")
|
|
if (noPostsMessage) {
|
|
noPostsMessage.remove()
|
|
}
|
|
|
|
const html = `
|
|
<div class="flex ${isMe ? 'justify-end' : 'justify-start'} mb-4 message-item" id="post_${data.post_id}">
|
|
${!isMe ? `
|
|
<div class="flex-shrink-0 mr-3">
|
|
<span class="inline-flex items-center justify-center h-8 w-8 rounded-full text-xs font-bold border border-gray-300 bg-white text-gray-700 shadow-sm" title="${data.display_name || data.power || '?'}">
|
|
${(data.display_name || data.power || '?').substring(0, 2)}
|
|
</span>
|
|
</div>
|
|
` : ''}
|
|
|
|
<div class="max-w-lg ${isMe ? 'order-1' : 'order-2'}">
|
|
<div class="flex items-baseline space-x-2 mb-1 ${isMe ? 'justify-end' : 'justify-start'}">
|
|
${!isMe ? `<span class="text-xs font-bold text-gray-900">${data.display_name || data.power || '?'}</span>` : ''}
|
|
<span class="text-xs text-gray-500">${data.created_at}</span>
|
|
${data.phase ? `<span class="text-xs bg-gray-100 px-1 rounded text-gray-600 border border-gray-200">${data.phase}</span>` : ''}
|
|
</div>
|
|
|
|
<div class="px-4 py-2 rounded-lg shadow-sm text-sm ${isMe ? 'bg-indigo-600 text-white rounded-br-none' : 'bg-white border border-gray-200 text-gray-900 rounded-bl-none'}">
|
|
<div>${this._escapeHtml(data.body).replace(/\n/g, '<br>')}</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
`
|
|
|
|
this.element.insertAdjacentHTML('afterbegin', html)
|
|
// this.element.scrollTop = 0 // 必要ならトップへスクロール
|
|
}
|
|
|
|
_escapeHtml(unsafe) {
|
|
return unsafe
|
|
.replace(/&/g, "&")
|
|
.replace(/</g, "<")
|
|
.replace(/>/g, ">")
|
|
.replace(/"/g, """)
|
|
.replace(/'/g, "'")
|
|
}
|
|
}
|