From 0c1d122aace47bdad4b81961df1630c13640e837 Mon Sep 17 00:00:00 2001 From: Koji Ishimoto Date: Wed, 28 Feb 2024 03:08:31 +0900 Subject: [PATCH] =?UTF-8?q?Comment=E9=96=A2=E9=80=A3=E3=81=AEReact?= =?UTF-8?q?=E3=82=B3=E3=83=B3=E3=83=9D=E3=83=BC=E3=83=8D=E3=83=B3=E3=83=88?= =?UTF-8?q?=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/Comments/Comment.jsx | 67 ++++++++ .../components/Comments/CommentForm.jsx | 143 ++++++++++++++++ .../Comments/CommentPlaceholder.jsx | 45 ++++++ .../components/Comments/Comments.jsx | 152 ++++++++++++++++++ .../components/Comments/Editing.jsx | 74 +++++++++ app/javascript/components/Comments/Share.jsx | 64 ++++++++ .../components/Comments/Viewing.jsx | 112 +++++++++++++ .../components/Comments/commentApi.js | 65 ++++++++ .../components/Comments/useComment.js | 93 +++++++++++ app/views/announcements/show.html.slim | 2 + app/views/events/show.html.slim | 12 +- app/views/pages/show.html.slim | 3 + app/views/products/show.html.slim | 4 + app/views/regular_events/show.html.slim | 12 +- app/views/reports/show.html.slim | 3 + app/views/talks/show.html.slim | 2 + 16 files changed, 843 insertions(+), 10 deletions(-) create mode 100644 app/javascript/components/Comments/Comment.jsx create mode 100644 app/javascript/components/Comments/CommentForm.jsx create mode 100644 app/javascript/components/Comments/CommentPlaceholder.jsx create mode 100644 app/javascript/components/Comments/Comments.jsx create mode 100644 app/javascript/components/Comments/Editing.jsx create mode 100644 app/javascript/components/Comments/Share.jsx create mode 100644 app/javascript/components/Comments/Viewing.jsx create mode 100644 app/javascript/components/Comments/commentApi.js create mode 100644 app/javascript/components/Comments/useComment.js diff --git a/app/javascript/components/Comments/Comment.jsx b/app/javascript/components/Comments/Comment.jsx new file mode 100644 index 00000000000..6559d3fcd49 --- /dev/null +++ b/app/javascript/components/Comments/Comment.jsx @@ -0,0 +1,67 @@ +import React, { useState } from 'react' +import { CommentUserIcon, CommentThreadStart, CommentThreadEnd } from './Share' +import { CommentViewing } from './Viewing' +import { CommentEditing } from './Editing' +import clsx from 'clsx' + +const CommentCompanyLink = ({ user }) => { + if (!user.company || !user.adviser) return <> + return ( + + + + ) +} + +const Comment = ({ + comment, + currentUser, + isLatest, + isValidating, + availableEmojis, + onDeleteComment, + onUpdateComment, + className, + ...props +}) => { + const [editing, setEditing] = useState(false) + + return ( +
+ {/* + id="latest-comment"は最新のコメントへのリンク先です + アプリ内の複数箇所でリンク先として設定されています + */} + {isLatest &&
} + + + + + + + + {editing + ? setEditing(false)} + onUpdateComment={onUpdateComment} + /> + : setEditing(true)} + onDeleteComment={onDeleteComment} + />} + +
+ ) +} + +export { + Comment, +} diff --git a/app/javascript/components/Comments/CommentForm.jsx b/app/javascript/components/Comments/CommentForm.jsx new file mode 100644 index 00000000000..5f010a6d597 --- /dev/null +++ b/app/javascript/components/Comments/CommentForm.jsx @@ -0,0 +1,143 @@ +import React, { useState, useRef } from 'react' +import { + CommentThreadStart, + CommentThreadEnd, + CommentUserIcon, + CommentTab, +} from './Share' +import * as Markdown from '../Markdown' +import * as Card from '../ui/Card' +import { useTextarea } from './useTextarea' +import toast from '../../toast' + +/* コメントフォーム */ +const CommentForm = ({ + onCreateCommentAndWatch, + onCreateCheck, + onBecomeResponsibleMentor, + isCheckable, + isPreventCommentAndCheck, + currentUser, +}) => { + const [description, setDescription] = useState('') + const isValidDescrption = description.length > 0 + // 'comment' | 'preview' + const [activeTab, setActiveTab] = useState('comment') + const isActive = (tab) => tab === activeTab + const textareaRef = useRef(null) + const previewRef = useRef(null) + useTextarea(textareaRef) + const [isPosting, setIsPosting] = useState(false) + + // Markdownのプレビュー要素の削除 + const removePreviewLastChild = () => { + while (previewRef.current.lastChild) { + previewRef.current.removeChild(previewRef.current.lastChild) + } + } + + const handleClickCreateComment = () => { + setIsPosting(true) + try { + // コメントの作成とWatchをして、担当者になる + onCreateCommentAndWatch(description) + onBecomeResponsibleMentor() + toast.methods.toast('コメントを投稿して担当者になりました') + } catch (error) { + toast.methods.toast('コメントの投稿に失敗しました', 'error') + } finally { + // クリーンアップ + setDescription('') + removePreviewLastChild() + setActiveTab('comment') + setIsPosting(false) + // this.resizeTextarea() + } + } + + const handleClickCreateCommentAndCheck = () => { + if (isPreventCommentAndCheck()) return + setIsPosting(true) + try { + // コメントの作成とWatchをして、確認OKにする + onCreateCommentAndWatch(description) + onCreateCheck() + toast.methods.toast('コメントを投稿して確認OKにしました') + } catch (error) { + toast.methods.toast('コメントの投稿に失敗しました', 'error') + } finally { + // クリーンアップ + setDescription('') + removePreviewLastChild() + setActiveTab('comment') + setIsPosting(false) + // this.resizeTextarea() + } + } + + return ( +
+ {/* ログインユーザーのプロフィール画像 */} + + + + {/* コメントフォーム本体 */} + + + {/* フォームのエディターモードとプレビューモードの切り替え */} + + {/* Markdownエディターとプレビュー */} + + + + + + + + + + + + + {/* コメント送信ボタン */} + + + + {/* コメント・確認OK送信ボタン */} + {isCheckable && + + + + } + + + +
+ ) +} + +export default CommentForm diff --git a/app/javascript/components/Comments/CommentPlaceholder.jsx b/app/javascript/components/Comments/CommentPlaceholder.jsx new file mode 100644 index 00000000000..d90bd762b70 --- /dev/null +++ b/app/javascript/components/Comments/CommentPlaceholder.jsx @@ -0,0 +1,45 @@ +import React from 'react' + +const Placeholder = () => { + return ( +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+

+

+

+

+

+
+
+
+
+
+ ) +} + +const CommentPlaceholder = () => { + const placeholderCount = 3 + return( +
+ {Array.from({ length: placeholderCount }, (_, index) => ( + + ))} +
+ ) +} + +export default CommentPlaceholder diff --git a/app/javascript/components/Comments/Comments.jsx b/app/javascript/components/Comments/Comments.jsx new file mode 100644 index 00000000000..7bb2ab42967 --- /dev/null +++ b/app/javascript/components/Comments/Comments.jsx @@ -0,0 +1,152 @@ +import React from 'react' +import { Comment } from './Comment' +import CommentForm from './CommentForm' +import CommentPlaceholder from './CommentPlaceholder' +import clsx from 'clsx' +import { useComment } from './useComment' +import { useCheck } from '../Check/useCheck' +import { useResponsibleMentor } from '../ResponsibleMentor/useResponsibleMentor' +import { useZustandStore } from '../../hooks/useZustandStore' + +const CommentsHeader = ({ className, children, ...props }) => { + return ( +
+

+ {children} +

+
+ ) +} + +const CommentsLoadMore = ({ onClick, nextCommentAmount }) => { + return( +
+
+
+ +
+
+
+ ) +} + +const CommentsList = ({ className, children, ...props }) => { + return ( +
+ {children} +
) +} + +const Comments = ({ + title, + commentableId, + commentableType, + currentUser, + availableEmojis +}) => { + const { + comments, + error, + isLoading, + isValidating, + nextCommentAmount, + handleLoadMore, + handleCreateComment, + handleUpdateComment, + handleDeleteComment + } = useComment({ + currentUserId: currentUser.id, + commentableType, + commentableId + }) + const { isChecked, handleCreateCheck } = useCheck() + const { + productId, + handleBecomeResponsibleMentor, + responsibleMentorState + } = useResponsibleMentor() + const { setWatchable } = useZustandStore((state) => state.watch) + const isCheckable = currentUser.roles.includes("mentor") + && /^(Report|Product)$/.test(commentableType) + && !isChecked + + if (error) return <>エラーが発生しました。 + if (isLoading) return + + return ( +
+ {/* + 表示していないコメントを読み込むためのボタン + 現在は8件ごとにコメントを表示しており + 表示していないコメントが無いと消えます + */} + {nextCommentAmount > 0 && + handleLoadMore()} + nextCommentAmount={nextCommentAmount} + /> + } + {/* コメント欄のタイトル */} + {title} + {/* コメント一覧 */} + + {comments.reverse().map(({ comments, comment_total_count: _commentTotalCount }) => { + return comments.reverse().map((comment, index) => { + return ( + { + if (window.confirm('削除してよろしいですか?')) { + handleDeleteComment(comment.id) + } + }} + onUpdateComment={handleUpdateComment} + />) + }) + })} + + {/* コメント投稿フォーム */} + { + const isConfirmed = ()=> window.confirm('提出物を確認済にしてよろしいですか?') + return commentableType === 'Product' && !isConfirmed() + }} + onCreateCommentAndWatch={async () => { + await handleCreateComment() + // Commentが作成されるとBackendでは自動でWatchも作成されるので、 + // フロントのZustandでも合わせてWatchする + setWatchable({ watchableId: commentableId, watchableType: commentableType }) + }} + onCreateCheck={handleCreateCheck} + onBecomeResponsibleMentor={() => { + // 提出物のコメントで、担当者がおらずに確認が済んでいない場合にtrue + const isBecomeResponsibleMentor = commentableType === 'Product' + && responsibleMentorState === 'absent' + && isChecked === false + if (isBecomeResponsibleMentor) { + handleBecomeResponsibleMentor({ productId, currentUser: currentUser.id }) + } + }} + currentUser={currentUser} + /> +
+ ) +} + +export default Comments diff --git a/app/javascript/components/Comments/Editing.jsx b/app/javascript/components/Comments/Editing.jsx new file mode 100644 index 00000000000..0463cb5016d --- /dev/null +++ b/app/javascript/components/Comments/Editing.jsx @@ -0,0 +1,74 @@ +import React, { useState, useRef } from 'react' +import 'dayjs/locale/ja' +import { CommentTab } from './Share' +import * as Card from '../ui/Card' +import { Border } from '../ui/Border' +import * as Markdown from '../Markdown' +import { useTextarea } from './useTextarea' + +const CommentEditing = ({ + comment, + isValidating, + onStopEditing, + onUpdateComment, +}) => { + // 'comment' | 'preview' + const [activeTab, setActiveTab] = useState('comment') + const [description, setDescription] = useState(comment.description) + const isActive = (tab) => tab === activeTab + const textareaRef = useRef(null) + useTextarea(textareaRef) + + return( + +
+ + + + + + + + + + + + + + + + + + + + + +
+
+ ) +} + +export { CommentEditing } diff --git a/app/javascript/components/Comments/Share.jsx b/app/javascript/components/Comments/Share.jsx new file mode 100644 index 00000000000..cdee70d8075 --- /dev/null +++ b/app/javascript/components/Comments/Share.jsx @@ -0,0 +1,64 @@ +import React from 'react' +import clsx from 'clsx' + +const CommentThreadStart = ({children}) => { + return ( +
+ {children} +
+ ) +} + +const CommentThreadEnd = ({children}) => { + return ( +
+ {children} +
+ ) +} + +// 画面幅が48em以上の場合のみ表示されるコンポーネント +// cssではなくてuseMediaQueryのようなものを使って +// react hooksで制御した方が分かり易いかもしれない +// https://github.com/yocontra/react-responsive +const CommentUserIcon = ({ user }) => { + const roleClass = `is-${user.primary_role}` + + return ( + + {user.icon_title} + + ) +} + +const CommentTab = ({ isActive, setActiveTab }) => { + return ( +
+
setActiveTab('comment')} + > + コメント +
+
setActiveTab('preview')} + > + プレビュー +
+
+ ) +} + + +export { + CommentThreadStart, + CommentThreadEnd, + CommentUserIcon, + CommentTab, +} diff --git a/app/javascript/components/Comments/Viewing.jsx b/app/javascript/components/Comments/Viewing.jsx new file mode 100644 index 00000000000..83004a61a25 --- /dev/null +++ b/app/javascript/components/Comments/Viewing.jsx @@ -0,0 +1,112 @@ +import React from 'react' +import MarkdownInitializer from '../../markdown-initializer' // Import your MarkdownInitializer here +// components +import Reactions from '../Reactions/Reactions' +import { TimeClipboard } from '../ui/TimeClipboard' +import * as Card from '../ui/Card' +import { Border } from '../ui/Border' +// utils +import clsx from 'clsx' + +const Description = ({ comment }) => { + const markdownDescription = new MarkdownInitializer().render(comment.description) + + return ( +
+ {/* コメントしたユーザーがアドバイザーの場合、会社情報を表示 */} + {comment.user.company && comment.user.adviser && ( + + + + )} +
+
+ ) +} + +const CommentViewing = ({ + comment, + currentUser, + availableEmojis, + onStartEditing, + onDeleteComment +}) => { + const commentURL = window.location.href.split('#')[0] + '#comment_' + comment.id + const roleClass = `is-${comment.user.primary_role}` + const isRole = (role) => currentUser.roles.includes(role) + + return ( + + + {/* コメントしたユーザーと時間の情報 */} +

+ + + + + {comment.user.login_name} + +

+ {/* + TODO 何故かidのurlへのリンクを開いてもリンク先へジャンプしてくれない + Vueとはコンポー年とのマウントのさせかたが違うのが影響している? + */} + {/* コメントした時間の表示と日付のクリップボードへのコピー機能 */} + +
+ + {/* コメントの表示 */} + + + {/* コメントへの絵文字でのリアクション */} +
+ +
+ + {/* コメントした本人か管理者だけに表示 */} + {(comment.user.id === currentUser.id || !isRole("admin")) && ( + + {/* コメントの編集開始ボタン */} + + + + {/* コメントの削除ボタン */} + + + + + )} +
+ ) +} + +export { CommentViewing } diff --git a/app/javascript/components/Comments/commentApi.js b/app/javascript/components/Comments/commentApi.js new file mode 100644 index 00000000000..16e151eee05 --- /dev/null +++ b/app/javascript/components/Comments/commentApi.js @@ -0,0 +1,65 @@ +import CSRF from '../../csrf' + +const createComment = async (description, commentableType, commentableId) => { + if (description.length < 1) { + return null + } + + const params = { + comment: { description: description }, + commentable_type: commentableType, + commentable_id: commentableId + } + + const response = await fetch(`/api/comments`, { + method: 'POST', + 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) + }) + return response.json() +} + +const deleteComment = async (id) => { + const response = await fetch(`/api/comments/${id}.json`, { + method: 'DELETE', + headers: { + 'Content-Type': 'application/json; charset=utf-8', + 'X-Requested-With': 'XMLHttpRequest', + 'X-CSRF-Token': CSRF.getToken() + }, + credentials: 'same-origin', + redirect: 'manual' + }) + return response +} + +const updateComment = async (id, description) => { + const params = { + comment: { description: description }, + } + + const response = await fetch(`/api/comments/${id}.json`, { + 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) + }) + return response +} + +export { + createComment, + updateComment, + deleteComment, +} diff --git a/app/javascript/components/Comments/useComment.js b/app/javascript/components/Comments/useComment.js new file mode 100644 index 00000000000..42f074c7aa3 --- /dev/null +++ b/app/javascript/components/Comments/useComment.js @@ -0,0 +1,93 @@ +import useSWRInfinite from 'swr/infinite' +import { createComment, deleteComment, updateComment } from './commentApi' +import toast from '../../toast' +import fetcher from '../../fetcher' + +export const useComment = ({ + commentableType, + commentableId +}) => { + // 1度に読み込むコメント数 + const paginationAmount = 8 + const apiUrl = '/api/comments.json?' + + const getKey = (pageIndex) => { + const search = new URLSearchParams({ + commentable_type: commentableType, + commentable_id: commentableId, + comment_limit: paginationAmount, + comment_offset: pageIndex * paginationAmount + }) + return apiUrl + search + } + + // useSWRInfiniteの使い方 https://swr.vercel.app/docs/pagination#useswrinfinite + const { + data: comments, + error, + isLoading, + isValidating, + mutate, + size, + setSize, + } = useSWRInfinite(getKey, fetcher, { + revalidateAll: true, + parallel: true + }) + + const nextCommentAmount = comments + ? comments[0]?.comment_total_count - (comments?.length * paginationAmount) + : 0 + + // コメントのページネーションを1つ進める + const handleLoadMore = () => setSize(size + 1) + + // 1. コメントの投稿 + // 2. swrのcacheの更新とrevalidation + // 3. コメントに紐付けられた投稿をwatchする + // 4. 提出物で担当者がおらず確認が済んでいない場合 + // 5. + const handleCreateComment = async (description) => { + await createComment(description, commentableType, commentableId) + // https://github.com/vercel/swr/issues/908 + mutate(comments, false) + mutate() + } + + const handleUpdateComment = (id, description) => { + updateComment(id, description) + .then(() => { + mutate(comments, false) + mutate() + toast.methods.toast('コメントを更新しました!') + }) + .catch((error) => { + console.error(error) + toast.methods.toast('コメントの更新に失敗しました', 'error') + }) + } + + const handleDeleteComment = (commentId) => { + deleteComment(commentId) + .then(() => { + mutate(comments, false) + mutate() + toast.methods.toast('コメントを削除しました!') + }) + .catch(() => { + toast.methods.toast('コメントの削除に失敗しました', 'error') + }) + } + + return { + comments, + error, + isLoading, + isValidating, + nextCommentAmount, + handleLoadMore, + handleCreateComment, + handleUpdateComment, + handleDeleteComment, + } +} diff --git a/app/views/announcements/show.html.slim b/app/views/announcements/show.html.slim index 3cc34e3aca0..f0f776f8038 100644 --- a/app/views/announcements/show.html.slim +++ b/app/views/announcements/show.html.slim @@ -18,4 +18,6 @@ hr.a-border .container.is-md = render 'announcement', announcement: @announcement #js-comments(data-commentable-id="#{@announcement.id}" data-commentable-type='Announcement' data-current-user-id="#{current_user.id}") + - narrowed_current_user = current_user.as_json(only: %i[id login_name], methods: %i[avatar_url roles primary_role icon_title]) + = react_component('Comments/Comments', title: '質問・コメント', commentableId: @announcement.id, commentableType: 'Announcement', currentUser: narrowed_current_user, availableEmojis: Reaction.available_emojis.to_json) div(data-vue='Footprints' data-vue-footprintable-id="#{@announcement.id}" data-vue-footprintable-type='Announcement') diff --git a/app/views/events/show.html.slim b/app/views/events/show.html.slim index 057429fb612..c972099329a 100644 --- a/app/views/events/show.html.slim +++ b/app/views/events/show.html.slim @@ -56,8 +56,10 @@ - else hr.a-border - .page-body - .container.is-md - = render 'event', event: @event - #js-comments(data-commentable-id="#{@event.id}" data-commentable-type='Event' data-current-user-id="#{current_user.id}") - div(data-vue='Footprints' data-vue-footprintable-id="#{@event.id}" data-vue-footprintable-type='Event') +.page-body + .container.is-md + = render 'event', event: @event + #js-comments(data-commentable-id="#{@event.id}" data-commentable-type='Event' data-current-user-id="#{current_user.id}") + - narrowed_current_user = current_user.as_json(only: %i[id login_name], methods: %i[avatar_url roles primary_role icon_title]) + = react_component('Comments/Comments', title: '質問・連絡・コメント', commentableId: @event.id, commentableType: 'Event', currentUser: narrowed_current_user, availableEmojis: Reaction.available_emojis.to_json) + div(data-vue='Footprints' data-vue-footprintable-id="#{@event.id}" data-vue-footprintable-type='Event') diff --git a/app/views/pages/show.html.slim b/app/views/pages/show.html.slim index 492a130caa5..eac7da1854b 100644 --- a/app/views/pages/show.html.slim +++ b/app/views/pages/show.html.slim @@ -45,6 +45,9 @@ hr.a-border | 一覧に戻る #js-comments(data-commentable-id="#{@page.id}" data-commentable-type='Page' data-current-user-id="#{current_user.id}") + - narrowed_current_user = current_user.as_json(only: %i[id login_name], methods: %i[avatar_url roles primary_role icon_title]) + + = react_component('Comments/Comments', title: 'コメント', commentableId: @page.id, commentableType: 'Page', currentUser: narrowed_current_user, availableEmojis: Reaction.available_emojis.to_json) - if @page.practice nav.a-side-nav diff --git a/app/views/products/show.html.slim b/app/views/products/show.html.slim index e8d89c291c5..6e08c975e57 100644 --- a/app/views/products/show.html.slim +++ b/app/views/products/show.html.slim @@ -45,6 +45,10 @@ header.page-header = render 'product_body', product: @product #js-comments(data-commentable-id="#{@product.id}" data-commentable-type='Product' data-current-user-id="#{current_user.id}") + - narrowed_current_user = current_user.as_json(only: %i[id login_name], methods: %i[avatar_url roles primary_role icon_title]) + + = react_component('Comments/Comments', title: 'コメント', commentableId: @product.id, commentableType: 'Product', currentUser: narrowed_current_user, availableEmojis: Reaction.available_emojis.to_json) + div(data-vue='Footprints' data-vue-footprintable-id="#{@product.id}" data-vue-footprintable-type='Product') .is-only-mentor(class="#{current_user.mentor? || current_user.admin? ? 'col-xl-5 col-xs-12 is-hidden-sm-down' : ''}") diff --git a/app/views/regular_events/show.html.slim b/app/views/regular_events/show.html.slim index ba8668b45b6..f46300c4bf8 100644 --- a/app/views/regular_events/show.html.slim +++ b/app/views/regular_events/show.html.slim @@ -36,8 +36,10 @@ - else hr.a-border - .page-body - .container.is-md - = render 'regular_event', regular_event: @regular_event - #js-comments(data-commentable-id="#{@regular_event.id}" data-commentable-type='RegularEvent' data-current-user-id="#{current_user.id}") - div(data-vue='Footprints' data-vue-footprintable-id="#{@regular_event.id}" data-vue-footprintable-type='RegularEvent') +.page-body + .container.is-md + = render 'regular_event', regular_event: @regular_event + #js-comments(data-commentable-id="#{@regular_event.id}" data-commentable-type='RegularEvent' data-current-user-id="#{current_user.id}") + - narrowed_current_user = current_user.as_json(only: %i[id login_name], methods: %i[avatar_url roles primary_role icon_title]) + = react_component('Comments/Comments', title: '質問・連絡・コメント', commentableId: @regular_event.id, commentableType: 'RegularEvent', currentUser: narrowed_current_user, availableEmojis: Reaction.available_emojis.to_json) + div(data-vue='Footprints' data-vue-footprintable-id="#{@regular_event.id}" data-vue-footprintable-type='RegularEvent') diff --git a/app/views/reports/show.html.slim b/app/views/reports/show.html.slim index 2fa5563232b..537e99fa076 100644 --- a/app/views/reports/show.html.slim +++ b/app/views/reports/show.html.slim @@ -37,6 +37,9 @@ i.fa-solid.fa-angle-right #js-comments(data-commentable-id="#{@report.id}" data-commentable-type='Report' data-current-user-id="#{current_user.id}") + - narrowed_current_user = current_user.as_json(only: %i[id login_name], methods: %i[avatar_url roles primary_role icon_title]) + + = react_component('Comments/Comments', title: 'コメント', commentableId: @report.id, commentableType: 'Report', currentUser: narrowed_current_user, availableEmojis: Reaction.available_emojis.to_json) div(data-vue='Footprints' data-vue-footprintable-id="#{@report.id}" data-vue-footprintable-type='Report') diff --git a/app/views/talks/show.html.slim b/app/views/talks/show.html.slim index 5f2aee6ab04..12b204f84c9 100644 --- a/app/views/talks/show.html.slim +++ b/app/views/talks/show.html.slim @@ -56,6 +56,8 @@ = link_to user_path(member.id), class: 'a-user-name' do = member.login_name #js-comments(data-commentable-id="#{@talk.id}" data-commentable-type='Talk' data-current-user-id="#{current_user.id}") + - narrowed_current_user = current_user.as_json(only: %i[id login_name], methods: %i[avatar_url roles primary_role icon_title]) + = react_component(title: '連絡・返信', 'Comments/Comments', commentableId: @talk.id, commentableType: 'Talk', currentUser: narrowed_current_user, availableEmojis: Reaction.available_emojis.to_json) - if current_user.admin? div(data-vue="ActionCompletedButton" data-vue-commentable-id:number="#{@talk.id}" data-vue-is-initial-action-completed:boolean="#{@talk.action_completed}")