Move chat attachments into the chat composer

toast-story
Alex Gleason 2023-01-25 15:45:33 -06:00
rodzic a68b794476
commit 382e464390
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 7211D1F99744FBB7
4 zmienionych plików z 57 dodań i 22 usunięć

Wyświetl plik

@ -26,6 +26,8 @@ interface ITextarea extends Pick<React.TextareaHTMLAttributes<HTMLTextAreaElemen
hasError?: boolean, hasError?: boolean,
/** Whether or not you can resize the teztarea */ /** Whether or not you can resize the teztarea */
isResizeable?: boolean, isResizeable?: boolean,
/** Textarea theme. */
theme?: 'default' | 'transparent',
} }
/** Textarea with custom styles. */ /** Textarea with custom styles. */
@ -37,6 +39,7 @@ const Textarea = React.forwardRef(({
autoGrow = false, autoGrow = false,
maxRows = 10, maxRows = 10,
minRows = 1, minRows = 1,
theme = 'default',
...props ...props
}: ITextarea, ref: React.ForwardedRef<HTMLTextAreaElement>) => { }: ITextarea, ref: React.ForwardedRef<HTMLTextAreaElement>) => {
const [rows, setRows] = useState<number>(autoGrow ? 1 : 4); const [rows, setRows] = useState<number>(autoGrow ? 1 : 4);
@ -72,9 +75,10 @@ const Textarea = React.forwardRef(({
ref={ref} ref={ref}
rows={rows} rows={rows}
onChange={handleChange} onChange={handleChange}
className={classNames({ 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 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': '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':
true, theme === 'default',
'bg-transparent border-0 focus:border-0 focus:ring-0': theme === 'transparent',
'font-mono': isCodeEditor, 'font-mono': isCodeEditor,
'text-red-600 border-red-600': hasError, 'text-red-600 border-red-600': hasError,
'resize-none': !isResizeable, 'resize-none': !isResizeable,

Wyświetl plik

@ -3,13 +3,16 @@ import { defineMessages, IntlShape, useIntl } from 'react-intl';
import { unblockAccount } from 'soapbox/actions/accounts'; import { unblockAccount } from 'soapbox/actions/accounts';
import { openModal } from 'soapbox/actions/modals'; 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 { useChatContext } from 'soapbox/contexts/chat-context';
import UploadButton from 'soapbox/features/compose/components/upload-button'; import UploadButton from 'soapbox/features/compose/components/upload-button';
import { search as emojiSearch } from 'soapbox/features/emoji/emoji-mart-search-light'; import { search as emojiSearch } from 'soapbox/features/emoji/emoji-mart-search-light';
import { useAppDispatch, useAppSelector, useFeatures } from 'soapbox/hooks'; import { useAppDispatch, useAppSelector, useFeatures } from 'soapbox/hooks';
import { Attachment } from 'soapbox/types/entities';
import { textAtCursorMatchesToken } from 'soapbox/utils/suggestions'; import { textAtCursorMatchesToken } from 'soapbox/utils/suggestions';
import ChatTextarea from './chat-textarea';
const messages = defineMessages({ const messages = defineMessages({
placeholder: { id: 'chat.input.placeholder', defaultMessage: 'Type a message' }, placeholder: { id: 'chat.input.placeholder', defaultMessage: 'Type a message' },
send: { id: 'chat.actions.send', defaultMessage: 'Send' }, send: { id: 'chat.actions.send', defaultMessage: 'Send' },
@ -39,7 +42,7 @@ interface IChatComposer extends Pick<React.TextareaHTMLAttributes<HTMLTextAreaEl
errorMessage: string | undefined errorMessage: string | undefined
onSelectFile: (files: FileList, intl: IntlShape) => void onSelectFile: (files: FileList, intl: IntlShape) => void
resetFileKey: number | null resetFileKey: number | null
hasAttachment?: boolean attachments?: Attachment[]
} }
/** Textarea input for chats. */ /** Textarea input for chats. */
@ -53,7 +56,7 @@ const ChatComposer = React.forwardRef<HTMLTextAreaElement | null, IChatComposer>
onSelectFile, onSelectFile,
resetFileKey, resetFileKey,
onPaste, onPaste,
hasAttachment, attachments = [],
}, ref) => { }, ref) => {
const intl = useIntl(); const intl = useIntl();
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
@ -68,6 +71,7 @@ const ChatComposer = React.forwardRef<HTMLTextAreaElement | null, IChatComposer>
const [suggestions, setSuggestions] = useState<Suggestion>(initialSuggestionState); const [suggestions, setSuggestions] = useState<Suggestion>(initialSuggestionState);
const isSuggestionsAvailable = suggestions.list.length > 0; const isSuggestionsAvailable = suggestions.list.length > 0;
const hasAttachment = attachments.length > 0;
const isOverCharacterLimit = maxCharacterCount && value?.length > maxCharacterCount; const isOverCharacterLimit = maxCharacterCount && value?.length > maxCharacterCount;
const isSubmitDisabled = disabled || isOverCharacterLimit || (value.length === 0 && !hasAttachment); const isSubmitDisabled = disabled || isOverCharacterLimit || (value.length === 0 && !hasAttachment);
@ -167,12 +171,9 @@ const ChatComposer = React.forwardRef<HTMLTextAreaElement | null, IChatComposer>
)} )}
<Stack grow> <Stack grow>
<Combobox <Combobox onSelect={onSelectComboboxOption}>
aria-labelledby='demo'
onSelect={onSelectComboboxOption}
>
<ComboboxInput <ComboboxInput
as={Textarea} as={ChatTextarea}
autoFocus autoFocus
ref={ref} ref={ref}
placeholder={intl.formatMessage(messages.placeholder)} placeholder={intl.formatMessage(messages.placeholder)}
@ -184,6 +185,7 @@ const ChatComposer = React.forwardRef<HTMLTextAreaElement | null, IChatComposer>
autoGrow autoGrow
maxRows={5} maxRows={5}
disabled={disabled} disabled={disabled}
attachments={attachments}
/> />
{isSuggestionsAvailable ? ( {isSuggestionsAvailable ? (
<ComboboxPopover> <ComboboxPopover>

Wyświetl plik

@ -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;

Wyświetl plik

@ -164,16 +164,6 @@ const Chat: React.FC<ChatInterface> = ({ chat, inputRef, className }) => {
<ChatMessageList chat={chat} /> <ChatMessageList chat={chat} />
</div> </div>
{attachment && (
<div className='relative h-48'>
<Upload
media={attachment}
onDelete={handleRemoveFile}
withPreview
/>
</div>
)}
{isUploading && ( {isUploading && (
<div className='p-4'> <div className='p-4'>
<UploadProgress progress={uploadProgress * 100} /> <UploadProgress progress={uploadProgress * 100} />
@ -190,7 +180,7 @@ const Chat: React.FC<ChatInterface> = ({ chat, inputRef, className }) => {
onSelectFile={handleFiles} onSelectFile={handleFiles}
resetFileKey={resetFileKey} resetFileKey={resetFileKey}
onPaste={handlePaste} onPaste={handlePaste}
hasAttachment={!!attachment} attachments={attachment ? [attachment] : []}
/> />
</Stack> </Stack>
); );