Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

個別のお問い合わせにコメントがつけられるようにした #8262

Open
wants to merge 27 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
13a721b
inquiryモデルにcommentとのポリモーフィック関係の記述を追加
ayu-0505 Dec 12, 2024
409b742
comment関係のviewsを追加し、お問い合わせの画面でコメントが表示できるように変更
ayu-0505 Dec 12, 2024
5f2ced8
comments_at_inquiry.jsを作成、読み込み時のプレースホルダーを表示
ayu-0505 Dec 12, 2024
61c7a7b
一部のタイプミスを修正
ayu-0505 Dec 13, 2024
c0f349a
多量コメント時の前コメント読み込みボタンの文言に対し、表示前コメント数が表示されるように変更
ayu-0505 Dec 13, 2024
1141b0f
一部のタイプミスを修正
ayu-0505 Dec 13, 2024
4568eae
コメント新規作成処理を追加
ayu-0505 Dec 13, 2024
5132b87
編集・削除機能を追加、JSのタブ表示切り替えを追加
ayu-0505 Dec 16, 2024
3100cd2
コメント読み込みボタンを実装
ayu-0505 Dec 19, 2024
19bb4cd
メソッド名やファイル名を実態に沿うように変更
ayu-0505 Dec 19, 2024
44065bb
一部の処理を関数化、マジックナンバー部分を変数に変更、該当URL以外で読み込まれないように早期リターンを追加
ayu-0505 Dec 19, 2024
0ba396b
setComments()メソッドの位置を変更
ayu-0505 Dec 20, 2024
6d2fe83
create後のレシーバーメソッドを変更
ayu-0505 Dec 20, 2024
1619625
コメント新規作成時にリロードなしで新規コメントが追加され、コメントフォームがリセットされるように変更
ayu-0505 Dec 22, 2024
c34af3e
新規作成してすぐのコメントでもリアクションボタンが使えるように変更
ayu-0505 Dec 27, 2024
a7384ea
作成時間を押すとURLがコピーできるように変更
ayu-0505 Dec 27, 2024
81ef463
不要なコメントを削除、使用順になるように関数位置を変更、viewパーシャルにて使用しないローカル変数を削除
ayu-0505 Dec 27, 2024
41ac5d1
comments_controller.rbのcreateにおいてInquiry分岐に関するコメントを追加
ayu-0505 Jan 6, 2025
8d0e27f
inquiryに対してコメントのテストを追加
ayu-0505 Jan 12, 2025
d8049b6
間違いがあったテストの修正、navigator.clipboardメソッドが使用できるようにJS用のドライバーをCapybaraに設定
ayu-0505 Jan 14, 2025
36bb881
テストで落ちている部分を修正
ayu-0505 Jan 14, 2025
0dd8081
ユーザーのコメントページにおいてInquiryに対するコメントを表示させないように変更
ayu-0505 Jan 14, 2025
5dff152
検索時にお問い合わせのコメントが表示されないように変更
ayu-0505 Jan 15, 2025
b71c67f
コメントの取得をコントローラーで行うように変更、コメントレンダー時にcollectionオプションを使うように変更
ayu-0505 Jan 15, 2025
972e7b9
テストコードデバッグのためのコードを削除
ayu-0505 Jan 15, 2025
7c305cf
Lintのインデントずれの指摘を修正
ayu-0505 Jan 15, 2025
c9f979d
画像が表示されずテキストで認識されていたので修正
ayu-0505 Jan 20, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions app/controllers/admin/inquiries_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,6 @@ def index

def show
@inquiry = Inquiry.find(params[:id])
@comments = @inquiry.comments.order(created_at: :asc)
end
end
7 changes: 6 additions & 1 deletion app/controllers/api/comments_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,12 @@ def create
@comment.user = current_user
@comment.commentable = commentable
if @comment.save
render :create, status: :created
# Inquiry以外はvueでJSONを使用している。コメント機能のJS変換が完了すれば下の'Inquiry'による分岐は不要なので削除してください。
if params[:commentable_type] == 'Inquiry'
Copy link
Contributor Author

@ayu-0505 ayu-0505 Jan 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

こちらのInquiryによる分岐ですが、bootcampアプリ自体の機能のための分岐ではなく、「コメント機能をvueからJS、Hotwireに変更していく過程」で発生している分岐です。
本来は不必要な分岐のため、上記のようにコード内コメントで注釈をつけています。

render partial: 'comments/comment', locals: { commentable:, comment: @comment, user: current_user }, status: :created
else
render :create, status: :created
end
else
head :bad_request
end
Expand Down
2 changes: 1 addition & 1 deletion app/controllers/users/comments_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ def set_user
def set_comments
@comments =
Comment
.where.not(commentable_type: 'Talk')
.where.not(commentable_type: %w[Talk Inquiry])
.preload(commentable: { user: { avatar_attachment: :blob } })
.eager_load(:user)
.where(user_id: user)
Expand Down
94 changes: 94 additions & 0 deletions app/javascript/comments_at_inquiry.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import initializeComment from 'initialize_comment.js'

document.addEventListener('DOMContentLoaded', () => {
const comments = document.querySelectorAll('.comment')
const loadingContent = document.querySelector('.loading-content')
if (!loadingContent) {
return
}
const commentContent = document.querySelector(
'#comments.thread-comments.loaded'
)
if (comments) {
loadingContent.style.display = 'none'
commentContent.style.display = 'block'
}

const initialComments = []
const commentTotalCount = comments.length
const initialLimit = 8
const incrementSize = 8
let commentRemaining = 0
const nextCommentAmount = 0

if (commentTotalCount <= initialLimit) {
comments.forEach((comment) => {
initialComments.push(comment)
})
} else {
for (let i = 1; i <= initialLimit; i++) {
initialComments.push(comments[commentTotalCount - i])
}
commentRemaining = commentTotalCount - initialLimit

const moreCommentButton = document.querySelector(
'.a-button.is-lg.is-text.is-block'
)
const moreComments = document.querySelector('.thread-comments-more')
if (commentRemaining > 0) {
moreComments.style.display = 'block'
}
displayMoreComments(commentRemaining, nextCommentAmount, moreCommentButton)
}
setComments(initialComments)

const moreCommentButton = document.querySelector(
'.a-button.is-lg.is-text.is-block'
)
const moreComments = document.querySelector('.thread-comments-more')
moreCommentButton.addEventListener('click', () => {
const nextComments = []
if (commentRemaining <= incrementSize) {
for (let i = 1; i <= commentRemaining; i++) {
nextComments.push(comments[commentRemaining - i])
}
setComments(nextComments)
commentRemaining = 0
moreComments.style.display = 'none'
} else {
for (let i = 1; i <= incrementSize; i++) {
nextComments.push(comments[commentRemaining - i])
}
commentRemaining = commentRemaining - incrementSize
setComments(nextComments)
displayMoreComments(
commentRemaining,
nextCommentAmount,
moreCommentButton
)
}
})

function displayMoreComments(
commentRemaining,
nextCommentAmount,
moreCommentButton
) {
if (commentRemaining <= incrementSize) {
nextCommentAmount = commentRemaining
const commentText = `前のコメント( ${nextCommentAmount} )`
moreCommentButton.textContent = commentText
} else {
nextCommentAmount = `${incrementSize} / ${commentRemaining}`
const commentText = `前のコメント( ${nextCommentAmount} )`
moreCommentButton.textContent = commentText
}
}

function setComments(comments) {
comments.forEach((comment) => {
comment.style.display = ''
initializeComment(comment)
})
}
})
159 changes: 159 additions & 0 deletions app/javascript/initialize_comment.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
import CSRF from 'csrf'
import TextareaInitializer from 'textarea-initializer'
import MarkdownInitializer from 'markdown-initializer'

export default function initializeComment(comment) {
const commentId = comment.dataset.comment_id
const commentDescription = comment.dataset.comment_description

let savedComment = ''
TextareaInitializer.initialize(`#js-comment-${commentId}`)
const markdownInitializer = new MarkdownInitializer()

const commentDisplay = comment.querySelector('.comment-display')
const commentEditor = comment.querySelector('.comment-editor')
const commentDisplayContent = commentDisplay.querySelector('.a-long-text')
commentDisplayContent.innerHTML =
markdownInitializer.render(commentDescription)

const commentEditorPreview = commentEditor.querySelector(
'.a-markdown-input__preview'
)
const editorTextarea = commentEditor.querySelector(
'.a-markdown-input__textarea'
)

if (commentDescription) {
commentDisplayContent.innerHTML =
markdownInitializer.render(commentDescription)
commentEditorPreview.innerHTML =
markdownInitializer.render(commentDescription)
}

const editButton = commentDisplay.querySelector('.card-main-actions__action')
const modalElements = [commentDisplay, commentEditor]
if (editButton) {
editButton.addEventListener('click', () => {
if (!savedComment) {
savedComment = editorTextarea.value
}
toggleVisibility(modalElements, 'is-hidden')
})
}

const saveButton = commentEditor.querySelector('.is-primary')
if (saveButton) {
saveButton.addEventListener('click', () => {
toggleVisibility(modalElements, 'is-hidden')
savedComment = editorTextarea.value
updateComment(commentId, savedComment)
commentDisplayContent.innerHTML = markdownInitializer.render(savedComment)
})
}

const cancelButton = commentEditor.querySelector('.is-secondary')
cancelButton.addEventListener('click', () => {
toggleVisibility(modalElements, 'is-hidden')
editorTextarea.value = savedComment
commentEditorPreview.innerHTML = markdownInitializer.render(savedComment)
})

editorTextarea.addEventListener('input', () => {
commentEditorPreview.innerHTML = markdownInitializer.render(
editorTextarea.value
)
})

const deleteButton = comment.querySelector('.card-main-actions__muted-action')
if (deleteButton) {
deleteButton.addEventListener('click', () => {
if (window.confirm('本当に宜しいですか?')) {
deleteComment(commentId)
}
})
}

const editTab = commentEditor.querySelector('.edit-comment-tab')
const editorTabContent = commentEditor.querySelector('.is-editor')
const previewTab = commentEditor.querySelector('.comment-preview-tab')
const previewTabContent = commentEditor.querySelector('.is-preview')

const tabElements = [editTab, editorTabContent, previewTab, previewTabContent]
editTab.addEventListener('click', () =>
toggleVisibility(tabElements, 'is-active')
)

previewTab.addEventListener('click', () =>
toggleVisibility(tabElements, 'is-active')
)

const createdAtElement = comment.querySelector('.thread-comment__created-at')
if (createdAtElement && navigator.clipboard) {
createdAtElement.addEventListener('click', () => {
const commentURL = location.href.split('#')[0] + '#comment_' + commentId
navigator.clipboard
.writeText(commentURL)
.then(() => {
createdAtElement.classList.add('is-active')
setTimeout(() => {
createdAtElement.classList.remove('is-active')
}, 4000)
})
.catch((error) => {
console.error(error)
})
})
}

function toggleVisibility(elements, className) {
elements.forEach((element) => {
element.classList.toggle(className)
})
}

function updateComment(commentId, description) {
if (description.length < 1) {
return null
}
const params = {
id: commentId,
comment: { description: description }
}
fetch(`/api/comments/${commentId}`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json; charset=utf-8',
'X-Requested-With': 'XMLHttpRequest',
'X-CSRF-Token': CSRF.getToken()
},
credentials: 'same-origin',
redirect: 'manual',
body: JSON.stringify(params)
}).catch((error) => {
console.warn(error)
})
}

function deleteComment(commentId) {
fetch(`/api/comments/${commentId}.json`, {
method: 'DELETE',
headers: {
'X-Requested-With': 'XMLHttpRequest',
'X-CSRF-Token': CSRF.getToken()
},
credentials: 'same-origin',
redirect: 'manual'
})
.then(() => {
const deletedComment = document.querySelector(
`.thread-comment.comment[data-comment_id='${commentId}']`
)
if (deletedComment) {
deletedComment.remove()
}
})
.catch((error) => {
console.warn(error)
})
}
}
Loading
Loading