Skip to content

Commit

Permalink
Comment関連のReactコンポーネントを追加
Browse files Browse the repository at this point in the history
  • Loading branch information
dowdiness committed Feb 27, 2024
1 parent e70a674 commit 0c1d122
Show file tree
Hide file tree
Showing 16 changed files with 843 additions and 10 deletions.
67 changes: 67 additions & 0 deletions app/javascript/components/Comments/Comment.jsx
Original file line number Diff line number Diff line change
@@ -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 (
<a className="thread-comment__company-link" href={user.url}>
<img className="thread-comment__company-logo" src={user.logo_url} />
</a>
)
}

const Comment = ({
comment,
currentUser,
isLatest,
isValidating,
availableEmojis,
onDeleteComment,
onUpdateComment,
className,
...props
}) => {
const [editing, setEditing] = useState(false)

return (
<div
className={clsx('thread-comment', { 'is-latest': isLatest }, className)}
{...props}
>
{/*
id="latest-comment"は最新のコメントへのリンク先です
アプリ内の複数箇所でリンク先として設定されています
*/}
{isLatest && <div id='latest-comment' />}
<CommentThreadStart>
<a className="thread-comment__user-link" href={comment.user.url}>
<CommentUserIcon user={comment.user} />
</a>
<CommentCompanyLink user={comment.user} />
</CommentThreadStart>
<CommentThreadEnd>
{editing
? <CommentEditing
comment={comment}
isValidating={isValidating}
onStopEditing={() => setEditing(false)}
onUpdateComment={onUpdateComment}
/>
: <CommentViewing
comment={comment}
currentUser={currentUser}
availableEmojis={availableEmojis}
onStartEditing={() => setEditing(true)}
onDeleteComment={onDeleteComment}
/>}
</CommentThreadEnd>
</div>
)
}

export {
Comment,
}
143 changes: 143 additions & 0 deletions app/javascript/components/Comments/CommentForm.jsx
Original file line number Diff line number Diff line change
@@ -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 (
<div className="thread-comment-form">
{/* ログインユーザーのプロフィール画像 */}
<CommentThreadStart>
<CommentUserIcon
user={currentUser}
/>
</CommentThreadStart>
{/* コメントフォーム本体 */}
<CommentThreadEnd>
<Card.Root className='thread-comment-form__form'>
{/* フォームのエディターモードとプレビューモードの切り替え */}
<CommentTab isActive={isActive} setActiveTab={setActiveTab}/>
{/* Markdownエディターとプレビュー */}
<Markdown.Root>
<Markdown.Item isActive={isActive('comment')}>
<Markdown.Form>
<Markdown.Textarea
id='js-new-comment'
variant='warning'
data-preview='#new-comment-preview'
data-input='#new-comment-file-input'
name='new_comment[description]'
description={description}
setDescription={setDescription}
ref={textareaRef}
/>
<Markdown.File id='new-comment-file-input' />
</Markdown.Form>
</Markdown.Item>
<Markdown.Item isActive={isActive('preview')}>
<Markdown.Preview id='new-comment-preview' ref={previewRef} />
</Markdown.Item>
</Markdown.Root>
<Card.Footer>
{/* コメント送信ボタン */}
<Card.FooterItem>
<button
id='js-shortcut-post-comment'
className='a-button is-sm is-primary is-block'
onClick={handleClickCreateComment}
disabled={!isValidDescrption || isPosting}
>
コメントする
</button>
</Card.FooterItem>
{/* コメント・確認OK送信ボタン */}
{isCheckable &&
<Card.FooterItem>
<button
className='a-button is-sm is-danger is-block'
onClick={handleClickCreateCommentAndCheck}
disabled={!isValidDescrption || isPosting}
>
<i className="fa-solid fa-check" /> 確認OKにする
</button>
</Card.FooterItem>
}
</Card.Footer>
</Card.Root>
</CommentThreadEnd>
</div>
)
}

export default CommentForm
45 changes: 45 additions & 0 deletions app/javascript/components/Comments/CommentPlaceholder.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import React from 'react'

const Placeholder = () => {
return (
<div className="thread-comment">
<div className="thread-comment__start">
<div className="thread-comment__user-icon a-user-icon a-placeholder"></div>
</div>
<div className="thread-comment__end">
<div className="a-card is-loading">
<div className="card-header">
<div className="thread-comment__title">
<div className="thread-comment__title-link a-placeholder"></div>
</div>
<div className="thread-comment__created-at a-placeholder"></div>
</div>
<hr className="a-border-tint" />
<div className="thread-comment__description">
<div className="a-long-text is-md a-placeholder">
<p></p>
<p></p>
<p></p>
<p></p>
<p></p>
<p></p>
</div>
</div>
</div>
</div>
</div>
)
}

const CommentPlaceholder = () => {
const placeholderCount = 3
return(
<div id="comments" className="thread-comments loading">
{Array.from({ length: placeholderCount }, (_, index) => (
<Placeholder key={index} />
))}
</div>
)
}

export default CommentPlaceholder
Loading

0 comments on commit 0c1d122

Please sign in to comment.