feat: edit/delete a Message (#30)
Co-authored-by: Walidoux <ma.walidkorchi@icloud.com>
This commit is contained in:
parent
38e227a9d4
commit
91a87199c4
@ -1,11 +1,16 @@
|
||||
import { useState, useRef } from 'react'
|
||||
import Image from 'next/image'
|
||||
import Link from 'next/link'
|
||||
import date from 'date-and-time'
|
||||
import TextareaAutosize from 'react-textarea-autosize'
|
||||
import useTranslation from 'next-translate/useTranslation'
|
||||
|
||||
import { MessageWithMember } from '../../../../models/Message'
|
||||
import { MessageText } from './MessageText'
|
||||
import { Loader } from '../../../design/Loader'
|
||||
import { MessageFile } from './MessageFile'
|
||||
import { Emoji } from '../../../Emoji'
|
||||
import { useAuthentication } from '../../../../tools/authentication'
|
||||
|
||||
export interface MessageProps {
|
||||
message: MessageWithMember
|
||||
@ -14,9 +19,60 @@ export interface MessageProps {
|
||||
export const Message: React.FC<MessageProps> = (props) => {
|
||||
const { message } = props
|
||||
|
||||
const textareaReference = useRef<HTMLTextAreaElement>(null)
|
||||
const [isEditing, setIsEditing] = useState(false)
|
||||
const { t } = useTranslation()
|
||||
const { authentication, user } = useAuthentication()
|
||||
|
||||
const handleDeleteMessage = async (): Promise<void> => {
|
||||
try {
|
||||
await authentication.api.delete(`/messages/${message.id}`)
|
||||
} catch {}
|
||||
}
|
||||
|
||||
const handleTextareaKeyDown: React.KeyboardEventHandler<HTMLFormElement> = (
|
||||
event
|
||||
) => {
|
||||
if (event.key === 'Enter' && !event.shiftKey) {
|
||||
event.preventDefault()
|
||||
event.currentTarget.dispatchEvent(
|
||||
new Event('submit', { cancelable: true, bubbles: true })
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const handleEdit = async (): Promise<void> => {
|
||||
const newMessage = textareaReference.current?.value ?? message.value
|
||||
if (
|
||||
typeof newMessage === 'string' &&
|
||||
newMessage.length > 0 &&
|
||||
newMessage !== message.value
|
||||
) {
|
||||
try {
|
||||
await authentication.api.put(`/messages/${message.id}`, {
|
||||
value: newMessage
|
||||
})
|
||||
} catch {}
|
||||
}
|
||||
handleEditMode()
|
||||
}
|
||||
|
||||
const handleEditSubmit: React.FormEventHandler<HTMLFormElement> = async (
|
||||
event
|
||||
) => {
|
||||
event.preventDefault()
|
||||
await handleEdit()
|
||||
}
|
||||
|
||||
const handleEditMode = (): void => {
|
||||
setIsEditing((oldIsEditing) => {
|
||||
return !oldIsEditing
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className='flex p-4 transition hover:bg-gray-200 dark:hover:bg-gray-900'
|
||||
className='group flex w-full p-4 transition hover:bg-gray-200 dark:hover:bg-gray-900'
|
||||
data-cy={`message-${message.id}`}
|
||||
>
|
||||
<Link href={`/application/users/${message.member.user.id}`}>
|
||||
@ -40,7 +96,7 @@ export const Message: React.FC<MessageProps> = (props) => {
|
||||
</div>
|
||||
</a>
|
||||
</Link>
|
||||
<div className='w-full'>
|
||||
<div className='relative w-full whitespace-pre-wrap break-words break-all'>
|
||||
<div className='flex w-max items-center'>
|
||||
<Link href={`/application/users/${message.member.user.id}`}>
|
||||
<a>
|
||||
@ -59,8 +115,58 @@ export const Message: React.FC<MessageProps> = (props) => {
|
||||
{date.format(new Date(message.createdAt), 'DD/MM/YYYY - HH:mm:ss')}
|
||||
</span>
|
||||
</div>
|
||||
{message.member.userId === user.id ? (
|
||||
<div className='absolute right-6 -top-8 flex opacity-0 transition-opacity group-hover:opacity-100'>
|
||||
{message.type === 'text' ? (
|
||||
<div
|
||||
className='message-options rounded-l-lg border-l-slate-600'
|
||||
title={t('application:edit')}
|
||||
onClick={isEditing ? handleEdit : handleEditMode}
|
||||
>
|
||||
<Emoji
|
||||
value={isEditing ? ':white_check_mark:' : ':pencil2:'}
|
||||
size={18}
|
||||
/>
|
||||
</div>
|
||||
) : null}
|
||||
<div
|
||||
className='message-options rounded-r-lg border-r-slate-600'
|
||||
title={t('application:delete')}
|
||||
onClick={handleDeleteMessage}
|
||||
>
|
||||
<Emoji value=':wastebasket:' size={18} />
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
{message.type === 'text' ? (
|
||||
<MessageText message={message} />
|
||||
<>
|
||||
{isEditing ? (
|
||||
<form
|
||||
className='flex h-full w-full items-center'
|
||||
onSubmit={handleEditSubmit}
|
||||
onKeyDown={handleTextareaKeyDown}
|
||||
>
|
||||
<TextareaAutosize
|
||||
className='scrollbar-firefox-support w-full resize-none bg-transparent p-2 tracking-wide outline-none'
|
||||
placeholder={t('application:write-a-message')}
|
||||
wrap='soft'
|
||||
maxRows={6}
|
||||
name='message'
|
||||
defaultValue={message.value}
|
||||
ref={textareaReference}
|
||||
autoFocus
|
||||
onFocus={(event) => {
|
||||
event.currentTarget.setSelectionRange(
|
||||
event.currentTarget.value.length,
|
||||
event.currentTarget.value.length
|
||||
)
|
||||
}}
|
||||
/>
|
||||
</form>
|
||||
) : (
|
||||
<MessageText message={message} />
|
||||
)}
|
||||
</>
|
||||
) : message.type === 'file' ? (
|
||||
<MessageFile message={message} />
|
||||
) : (
|
||||
|
@ -96,7 +96,7 @@ export const SendMessage: React.FC<SendMessageProps> = (props) => {
|
||||
onKeyDown={handleTextareaKeyDown}
|
||||
>
|
||||
<TextareaAutosize
|
||||
className='scrollbar-firefox-support my-2 w-full resize-none bg-transparent p-2 px-6 font-paragraph tracking-wide outline-none'
|
||||
className='scrollbar-firefox-support my-2 w-full resize-none bg-transparent p-2 px-6 tracking-wide outline-none'
|
||||
placeholder={t('application:write-a-message')}
|
||||
wrap='soft'
|
||||
maxRows={6}
|
||||
|
@ -5,17 +5,18 @@ import { EMOJI_SET } from './emojiPlugin'
|
||||
export interface EmojiProps {
|
||||
value: string
|
||||
size: number
|
||||
tooltip?: boolean
|
||||
}
|
||||
|
||||
export const Emoji: React.FC<EmojiProps> = (props) => {
|
||||
const { value, size } = props
|
||||
const { value, size, tooltip = false } = props
|
||||
|
||||
return (
|
||||
<EmojiMart
|
||||
set={EMOJI_SET}
|
||||
emoji={value}
|
||||
size={size}
|
||||
tooltip
|
||||
tooltip={tooltip}
|
||||
fallback={() => {
|
||||
return <>{value}</>
|
||||
}}
|
||||
|
@ -24,6 +24,7 @@
|
||||
"saved-information": "The information have been saved.",
|
||||
"success-email-changed": "Please check your emails to confirm your new email address. You are now signed out.",
|
||||
"delete": "Delete",
|
||||
"edit": "Edit",
|
||||
"signout": "Sign out",
|
||||
"signout-all-devices": "Sign out of all devices",
|
||||
"signin-with-an-account": "Sign in with an account",
|
||||
|
@ -24,6 +24,7 @@
|
||||
"saved-information": "Les informations ont été enregistrées.",
|
||||
"success-email-changed": "Veuillez vérifier vos emails pour confirmer votre nouvelle adresse email. Vous êtes maintenant déconnecté.",
|
||||
"delete": "Supprimer",
|
||||
"edit": "Modifier",
|
||||
"signout": "Se déconnecter",
|
||||
"signout-all-devices": "Se déconnecter de tous les appareils",
|
||||
"signin-with-an-account": "Se connecter avec un compte",
|
||||
|
@ -43,3 +43,7 @@ body {
|
||||
scrollbar-color: var(--scroll-bar-color) var(--scroll-bar-bg-color);
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.message-options {
|
||||
@apply flex h-16 w-9 cursor-pointer items-center justify-center bg-gray-200 transition-colors hover:bg-gray-300 dark:bg-slate-900 hover:dark:bg-slate-800;
|
||||
}
|
||||
|
Reference in New Issue
Block a user