kopia lustrzana https://gitlab.com/soapbox-pub/soapbox
Move chat attachments into the chat composer
rodzic
a68b794476
commit
382e464390
|
@ -26,6 +26,8 @@ interface ITextarea extends Pick<React.TextareaHTMLAttributes<HTMLTextAreaElemen
|
|||
hasError?: boolean,
|
||||
/** Whether or not you can resize the teztarea */
|
||||
isResizeable?: boolean,
|
||||
/** Textarea theme. */
|
||||
theme?: 'default' | 'transparent',
|
||||
}
|
||||
|
||||
/** Textarea with custom styles. */
|
||||
|
@ -37,6 +39,7 @@ const Textarea = React.forwardRef(({
|
|||
autoGrow = false,
|
||||
maxRows = 10,
|
||||
minRows = 1,
|
||||
theme = 'default',
|
||||
...props
|
||||
}: ITextarea, ref: React.ForwardedRef<HTMLTextAreaElement>) => {
|
||||
const [rows, setRows] = useState<number>(autoGrow ? 1 : 4);
|
||||
|
@ -72,9 +75,10 @@ const Textarea = React.forwardRef(({
|
|||
ref={ref}
|
||||
rows={rows}
|
||||
onChange={handleChange}
|
||||
className={classNames({
|
||||
'bg-white dark:bg-transparent shadow-sm block w-full sm:text-sm rounded-md text-gray-900 dark:text-gray-100 placeholder:text-gray-600 dark:placeholder:text-gray-600 border-gray-400 dark:border-gray-800 dark:ring-1 dark:ring-gray-800 focus:ring-primary-500 focus:border-primary-500 dark:focus:ring-primary-500 dark:focus:border-primary-500':
|
||||
true,
|
||||
className={classNames('block w-full rounded-md sm:text-sm text-gray-900 dark:text-gray-100 placeholder:text-gray-600 dark:placeholder:text-gray-600', {
|
||||
'bg-white dark:bg-transparent shadow-sm border-gray-400 dark:border-gray-800 dark:ring-1 dark:ring-gray-800 focus:ring-primary-500 focus:border-primary-500 dark:focus:ring-primary-500 dark:focus:border-primary-500':
|
||||
theme === 'default',
|
||||
'bg-transparent border-0 focus:border-0 focus:ring-0': theme === 'transparent',
|
||||
'font-mono': isCodeEditor,
|
||||
'text-red-600 border-red-600': hasError,
|
||||
'resize-none': !isResizeable,
|
||||
|
|
|
@ -3,13 +3,16 @@ import { defineMessages, IntlShape, useIntl } from 'react-intl';
|
|||
|
||||
import { unblockAccount } from 'soapbox/actions/accounts';
|
||||
import { openModal } from 'soapbox/actions/modals';
|
||||
import { Button, Combobox, ComboboxInput, ComboboxList, ComboboxOption, ComboboxPopover, HStack, IconButton, Stack, Text, Textarea } from 'soapbox/components/ui';
|
||||
import { Button, Combobox, ComboboxInput, ComboboxList, ComboboxOption, ComboboxPopover, HStack, IconButton, Stack, Text } from 'soapbox/components/ui';
|
||||
import { useChatContext } from 'soapbox/contexts/chat-context';
|
||||
import UploadButton from 'soapbox/features/compose/components/upload-button';
|
||||
import { search as emojiSearch } from 'soapbox/features/emoji/emoji-mart-search-light';
|
||||
import { useAppDispatch, useAppSelector, useFeatures } from 'soapbox/hooks';
|
||||
import { Attachment } from 'soapbox/types/entities';
|
||||
import { textAtCursorMatchesToken } from 'soapbox/utils/suggestions';
|
||||
|
||||
import ChatTextarea from './chat-textarea';
|
||||
|
||||
const messages = defineMessages({
|
||||
placeholder: { id: 'chat.input.placeholder', defaultMessage: 'Type a message' },
|
||||
send: { id: 'chat.actions.send', defaultMessage: 'Send' },
|
||||
|
@ -39,7 +42,7 @@ interface IChatComposer extends Pick<React.TextareaHTMLAttributes<HTMLTextAreaEl
|
|||
errorMessage: string | undefined
|
||||
onSelectFile: (files: FileList, intl: IntlShape) => void
|
||||
resetFileKey: number | null
|
||||
hasAttachment?: boolean
|
||||
attachments?: Attachment[]
|
||||
}
|
||||
|
||||
/** Textarea input for chats. */
|
||||
|
@ -53,7 +56,7 @@ const ChatComposer = React.forwardRef<HTMLTextAreaElement | null, IChatComposer>
|
|||
onSelectFile,
|
||||
resetFileKey,
|
||||
onPaste,
|
||||
hasAttachment,
|
||||
attachments = [],
|
||||
}, ref) => {
|
||||
const intl = useIntl();
|
||||
const dispatch = useAppDispatch();
|
||||
|
@ -68,6 +71,7 @@ const ChatComposer = React.forwardRef<HTMLTextAreaElement | null, IChatComposer>
|
|||
const [suggestions, setSuggestions] = useState<Suggestion>(initialSuggestionState);
|
||||
const isSuggestionsAvailable = suggestions.list.length > 0;
|
||||
|
||||
const hasAttachment = attachments.length > 0;
|
||||
const isOverCharacterLimit = maxCharacterCount && value?.length > maxCharacterCount;
|
||||
const isSubmitDisabled = disabled || isOverCharacterLimit || (value.length === 0 && !hasAttachment);
|
||||
|
||||
|
@ -167,12 +171,9 @@ const ChatComposer = React.forwardRef<HTMLTextAreaElement | null, IChatComposer>
|
|||
)}
|
||||
|
||||
<Stack grow>
|
||||
<Combobox
|
||||
aria-labelledby='demo'
|
||||
onSelect={onSelectComboboxOption}
|
||||
>
|
||||
<Combobox onSelect={onSelectComboboxOption}>
|
||||
<ComboboxInput
|
||||
as={Textarea}
|
||||
as={ChatTextarea}
|
||||
autoFocus
|
||||
ref={ref}
|
||||
placeholder={intl.formatMessage(messages.placeholder)}
|
||||
|
@ -184,6 +185,7 @@ const ChatComposer = React.forwardRef<HTMLTextAreaElement | null, IChatComposer>
|
|||
autoGrow
|
||||
maxRows={5}
|
||||
disabled={disabled}
|
||||
attachments={attachments}
|
||||
/>
|
||||
{isSuggestionsAvailable ? (
|
||||
<ComboboxPopover>
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
import React from 'react';
|
||||
|
||||
import { Textarea } from 'soapbox/components/ui';
|
||||
import { Attachment } from 'soapbox/types/entities';
|
||||
|
||||
interface IChatTextarea extends React.ComponentProps<typeof Textarea> {
|
||||
attachments?: Attachment[]
|
||||
}
|
||||
|
||||
/** Custom textarea for chats. */
|
||||
const ChatTextarea: React.FC<IChatTextarea> = ({ attachments, ...rest }) => {
|
||||
return (
|
||||
<div className={`
|
||||
bg-white
|
||||
dark:bg-transparent
|
||||
shadow-sm block w-full
|
||||
sm:text-sm rounded-md
|
||||
text-gray-900 dark:text-gray-100
|
||||
border-1
|
||||
placeholder:text-gray-600 dark:placeholder:text-gray-600 border-gray-400 dark:border-gray-800
|
||||
dark:ring-1 dark:ring-gray-800 focus-within:ring-primary-500 focus-within:border-primary-500
|
||||
dark:focus-within:ring-primary-500 dark:focus-within:border-primary-500
|
||||
`}
|
||||
>
|
||||
{attachments?.map(attachment => (
|
||||
<img
|
||||
className='w-8 h-8'
|
||||
key={attachment.id}
|
||||
src={attachment.url}
|
||||
alt=''
|
||||
/>
|
||||
))}
|
||||
|
||||
<Textarea theme='transparent' {...rest} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ChatTextarea;
|
|
@ -164,16 +164,6 @@ const Chat: React.FC<ChatInterface> = ({ chat, inputRef, className }) => {
|
|||
<ChatMessageList chat={chat} />
|
||||
</div>
|
||||
|
||||
{attachment && (
|
||||
<div className='relative h-48'>
|
||||
<Upload
|
||||
media={attachment}
|
||||
onDelete={handleRemoveFile}
|
||||
withPreview
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{isUploading && (
|
||||
<div className='p-4'>
|
||||
<UploadProgress progress={uploadProgress * 100} />
|
||||
|
@ -190,7 +180,7 @@ const Chat: React.FC<ChatInterface> = ({ chat, inputRef, className }) => {
|
|||
onSelectFile={handleFiles}
|
||||
resetFileKey={resetFileKey}
|
||||
onPaste={handlePaste}
|
||||
hasAttachment={!!attachment}
|
||||
attachments={attachment ? [attachment] : []}
|
||||
/>
|
||||
</Stack>
|
||||
);
|
||||
|
|
Ładowanie…
Reference in New Issue