Files
cadhub/app/web/src/components/PartProfile/PartProfile.js
2021-05-01 07:32:21 +10:00

292 lines
10 KiB
JavaScript

import { useState, useEffect, useRef } from 'react'
import { useAuth } from '@redwoodjs/auth'
import { Link, navigate, routes } from '@redwoodjs/router'
import Editor from 'rich-markdown-editor'
import Dialog from '@material-ui/core/Dialog'
import ImageUploader from 'src/components/ImageUploader'
import ConfirmDialog from 'src/components/ConfirmDialog'
import Breadcrumb from 'src/components/Breadcrumb'
import EmojiReaction from 'src/components/EmojiReaction'
import Button from 'src/components/Button'
import PartReactionsCell from '../PartReactionsCell'
import { countEmotes } from 'src/helpers/emote'
import { getActiveClasses } from 'get-active-classes'
const PartProfile = ({
userPart,
isEditable,
onSave,
onDelete,
onReaction,
onComment,
}) => {
const [comment, setComment] = useState('')
const [isConfirmDialogOpen, setIsConfirmDialogOpen] = useState(false)
const [isReactionsModalOpen, setIsReactionsModalOpen] = useState(false)
const [isInvalid, setIsInvalid] = useState(false)
const { currentUser } = useAuth()
const editorRef = useRef(null)
const canEdit = currentUser?.sub === userPart.id
const isImageEditable = !isEditable && canEdit // image is editable when not in profile edit mode in order to separate them as it's too hard too to upload an image to cloudinary temporarily until the use saves (and maybe have to clean up) for the time being
const part = userPart?.Part
const emotes = countEmotes(part?.Reaction)
const userEmotes = part?.userReactions.map(({ emote }) => emote)
useEffect(() => {
isEditable &&
!canEdit &&
navigate(
routes.part({ userName: userPart.userName, partTitle: part?.title })
)
}, [currentUser])
const [input, setInput] = useState({
title: part?.title,
mainImage: part?.mainImage,
description: part?.description,
userId: userPart?.id,
})
const setProperty = (property, value) =>
setInput({
...input,
[property]: value,
})
const onTitleChange = ({ target }) =>
setProperty('title', target.value.replace(/([^a-zA-Z\d_:])/g, '-'))
const onDescriptionChange = (description) =>
setProperty('description', description())
const onImageUpload = ({ cloudinaryPublicId }) => {
onSave(part?.id, { ...input, mainImage: cloudinaryPublicId })
}
// setProperty('mainImage', cloudinaryPublicId)
const onEditSaveClick = (hi) => {
// do a thing
if (isEditable) {
if (!input.title) {
setIsInvalid(true)
return
}
setIsInvalid(false)
onSave(part?.id, input)
return
}
navigate(
routes.editPart({ userName: userPart?.userName, partTitle: part?.title })
)
}
return (
<>
<div
className="grid mt-20 gap-8"
style={{ gridTemplateColumns: 'auto 12rem minmax(12rem, 42rem) auto' }}
>
{/* Side column */}
<aside className="col-start-2 relative">
<ImageUploader
className="rounded-half rounded-br-lg shadow-md border-2 border-gray-200 border-solid"
aspectRatio={1}
imageUrl={userPart?.image}
width={300}
/>
<h4 className="text-indigo-800 text-xl underline text-right py-4">
<Link to={routes.user({ userName: userPart?.userName })}>
{userPart?.name}
</Link>
</h4>
<div className="h-px bg-indigo-200 mb-4" />
<EmojiReaction
emotes={emotes}
userEmotes={userEmotes}
onEmote={onReaction}
onShowPartReactions={() => setIsReactionsModalOpen(true)}
/>
<Button
className="mt-6 ml-auto hover:shadow-lg bg-gradient-to-r from-transparent to-indigo-100"
shouldAnimateHover
iconName="chevron-down"
onClick={() => {
document.getElementById('comment-section').scrollIntoView()
}}
>
{userPart?.Part?.Comment.length} Comments
</Button>
<Link
to={routes.ide({
userName: userPart?.userName,
partTitle: part?.title,
})}
>
<Button
className="mt-4 ml-auto shadow-md hover:shadow-lg bg-indigo-200 w-full justify-end"
shouldAnimateHover
iconName="terminal"
onClick={() => {}}
>
Open IDE
</Button>
</Link>
{canEdit && (
<>
<Button
className="mt-4 ml-auto shadow-md hover:shadow-lg bg-indigo-200 relative z-20 w-full justify-end"
shouldAnimateHover
iconName={isEditable ? 'save' : 'pencil'}
onClick={onEditSaveClick}
>
{isEditable ? 'Save Details' : 'Edit Details'}
</Button>
{isEditable && (
<Button
className="mt-4 ml-auto shadow-md hover:shadow-lg bg-indigo-200 relative z-20 w-full justify-end"
shouldAnimateHover
iconName="x"
onClick={() =>
navigate(
routes.part({
userName: userPart.userName,
partTitle: part?.title,
})
)
}
>
Cancel
</Button>
)}
<Button
className="mt-4 ml-auto shadow-md hover:shadow-lg bg-red-200 relative z-20 w-full justify-end"
shouldAnimateHover
iconName={'trash'}
onClick={() => setIsConfirmDialogOpen(true)}
type="danger"
>
Delete
</Button>
</>
)}
{/* gray overlay */}
{isEditable && (
<div className="absolute inset-0 bg-gray-300 opacity-75 z-10 transform scale-x-110 -ml-1 -mt-2" />
)}
</aside>
{/* main project center column */}
<section className="col-start-3">
<Breadcrumb
className="inline"
onPartTitleChange={isEditable && onTitleChange}
userName={userPart?.userName}
partTitle={input?.title}
isInvalid={isInvalid}
/>
{!isEditable && part?.id && (
<ImageUploader
className="rounded-lg shadow-md border-2 border-gray-200 border-solid mt-8"
onImageUpload={onImageUpload}
aspectRatio={16 / 9}
isEditable={isImageEditable}
imageUrl={input?.mainImage}
width={1010}
/>
)}
<div className="text-gray-500 text-sm font-ropa-sans pt-8">
{isEditable ? 'Markdown supported' : ''}
</div>
<div
id="description-wrap"
name="description"
className="markdown-overrides rounded-lg shadow-md bg-white px-12 py-6 min-h-md"
onClick={(e) =>
e?.target?.id === 'description-wrap' &&
editorRef?.current?.focusAtEnd()
}
>
<Editor
ref={editorRef}
defaultValue={part?.description || ''}
readOnly={!isEditable}
onChange={onDescriptionChange}
/>
</div>
{/* comments */}
{!isEditable && (
<>
<div className="h-px bg-indigo-200 mt-8" />
<h3
className="text-indigo-800 text-lg font-roboto tracking-wider mb-4"
id="comment-section"
>
Comments
</h3>
<ul>
{part?.Comment.map(({ text, user, id }) => (
<li key={id} className="flex mb-6">
<div className="w-8 h-8 overflow-hidden rounded-full border border-indigo-300 shadow flex-shrink-0">
<ImageUploader
className=""
aspectRatio={1}
imageUrl={user?.image}
width={50}
/>
</div>
<div className="ml-4 font-roboto">
<div className="text-gray-800 font-bold text-lg mb-1">
<Link to={routes.user({ userName: user?.userName })}>
{user?.userName}
</Link>
</div>
<div className="text-gray-700 p-3 rounded bg-gray-200 shadow">
{text}
</div>
</div>
</li>
))}
</ul>
{currentUser && (
<>
<div className="mt-12 ml-12">
<textarea
className="w-full h-32 rounded-lg shadow-inner outline-none resize-none p-3"
placeholder="Leave a comment"
value={comment}
onChange={({ target }) => setComment(target.value)}
/>
</div>
<Button
className={getActiveClasses(
'ml-auto hover:shadow-lg bg-gray-300 mt-4 mb-20',
{ 'bg-indigo-200': currentUser }
)}
shouldAnimateHover
disabled={!currentUser}
iconName={'save'}
onClick={() => onComment(comment)}
>
Comment
</Button>
</>
)}
</>
)}
</section>
</div>
<ConfirmDialog
open={isConfirmDialogOpen}
onClose={() => setIsConfirmDialogOpen(false)}
onConfirm={onDelete}
message="Are you sure you want to delete? This action cannot be undone."
/>
<Dialog
open={isReactionsModalOpen}
onClose={() => setIsReactionsModalOpen(false)}
fullWidth={true}
maxWidth={'sm'}
>
<PartReactionsCell partId={userPart?.Part?.id} />
</Dialog>
</>
)
}
export default PartProfile