Lexical: Use in ComposeEventModal, style improvements, types
Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
This commit is contained in:
parent
2549e72843
commit
b15640603c
17 changed files with 100 additions and 123 deletions
|
@ -35,7 +35,6 @@ const DropdownMenuItem = ({ index, item, onClick }: IDropdownMenuItem) => {
|
|||
if (!item) return;
|
||||
if (onClick) onClick();
|
||||
|
||||
|
||||
if (item.to) {
|
||||
event.preventDefault();
|
||||
history.push(item.to);
|
||||
|
|
|
@ -15,7 +15,6 @@ const Portal: React.FC<IPortal> = ({ children }) => {
|
|||
setIsRendered(true);
|
||||
}, []);
|
||||
|
||||
|
||||
if (!isRendered) {
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -99,7 +99,6 @@ const findElementPosition = (el: HTMLElement) => {
|
|||
};
|
||||
};
|
||||
|
||||
|
||||
const getPointerPosition = (el: HTMLElement, event: MouseEvent & TouchEvent): Point => {
|
||||
const box = findElementPosition(el);
|
||||
const boxW = el.offsetWidth;
|
||||
|
|
|
@ -11,7 +11,6 @@ import { CompatRouter } from 'react-router-dom-v5-compat';
|
|||
// @ts-ignore: it doesn't have types
|
||||
import { ScrollContext } from 'react-router-scroll-4';
|
||||
|
||||
|
||||
import { loadInstance } from 'soapbox/actions/instance';
|
||||
import { fetchMe } from 'soapbox/actions/me';
|
||||
import { loadSoapboxConfig, getSoapboxConfig } from 'soapbox/actions/soapbox';
|
||||
|
|
|
@ -12,5 +12,4 @@ function parseEntitiesPath(expandedPath: ExpandedEntitiesPath) {
|
|||
};
|
||||
}
|
||||
|
||||
|
||||
export { parseEntitiesPath };
|
|
@ -37,7 +37,6 @@ const UserIndex: React.FC = () => {
|
|||
updateQuery();
|
||||
}, []);
|
||||
|
||||
|
||||
const hasMore = items.count() < total && !!next;
|
||||
|
||||
const showLoading = isLoading && items.isEmpty();
|
||||
|
|
|
@ -2,7 +2,6 @@ import userEvent from '@testing-library/user-event';
|
|||
import React from 'react';
|
||||
import { VirtuosoMockContext } from 'react-virtuoso';
|
||||
|
||||
|
||||
import { ChatContext } from 'soapbox/contexts/chat-context';
|
||||
import { normalizeChatMessage, normalizeInstance } from 'soapbox/normalizers';
|
||||
import { IAccount } from 'soapbox/queries/accounts';
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import clsx from 'clsx';
|
||||
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { defineMessages, FormattedMessage, MessageDescriptor, useIntl } from 'react-intl';
|
||||
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
|
||||
import { Link, useHistory } from 'react-router-dom';
|
||||
import { length } from 'stringz';
|
||||
|
||||
|
@ -45,9 +45,6 @@ import type { Emoji } from 'soapbox/features/emoji';
|
|||
const allowedAroundShortCode = '><\u0085\u0020\u00a0\u1680\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029\u0009\u000a\u000b\u000c\u000d';
|
||||
|
||||
const messages = defineMessages({
|
||||
placeholder: { id: 'compose_form.placeholder', defaultMessage: 'What\'s on your mind?' },
|
||||
pollPlaceholder: { id: 'compose_form.poll_placeholder', defaultMessage: 'Add a poll topic…' },
|
||||
eventPlaceholder: { id: 'compose_form.event_placeholder', defaultMessage: 'Post to this event' },
|
||||
spoiler_placeholder: { id: 'compose_form.spoiler_placeholder', defaultMessage: 'Write your warning here (optional)' },
|
||||
publish: { id: 'compose_form.publish', defaultMessage: 'Post' },
|
||||
publishLoud: { id: 'compose_form.publish_loud', defaultMessage: '{publish}!' },
|
||||
|
@ -74,12 +71,11 @@ const ComposeForm = <ID extends string>({ id, shouldCondense, autoFocus, clickab
|
|||
|
||||
const compose = useCompose(id);
|
||||
const showSearch = useAppSelector((state) => state.search.submitted && !state.search.hidden);
|
||||
const isModalOpen = useAppSelector((state) => !!(state.modals.size && state.modals.last()!.modalType === 'COMPOSE'));
|
||||
const maxTootChars = configuration.getIn(['statuses', 'max_characters']) as number;
|
||||
const scheduledStatusCount = useAppSelector((state) => state.scheduled_statuses.size);
|
||||
const features = useFeatures();
|
||||
|
||||
const { text, suggestions, spoiler, spoiler_text: spoilerText, privacy, focusDate, caretPosition, is_submitting: isSubmitting, is_changing_upload: isChangingUpload, is_uploading: isUploading, schedule: scheduledAt, group_id: groupId } = compose;
|
||||
const { text, spoiler, spoiler_text: spoilerText, privacy, focusDate, caretPosition, is_submitting: isSubmitting, is_changing_upload: isChangingUpload, is_uploading: isUploading, schedule: scheduledAt, group_id: groupId } = compose;
|
||||
const prevSpoiler = usePrevious(spoiler);
|
||||
|
||||
const hasPoll = !!compose.poll;
|
||||
|
@ -93,17 +89,6 @@ const ComposeForm = <ID extends string>({ id, shouldCondense, autoFocus, clickab
|
|||
const autosuggestTextareaRef = useRef<AutosuggestTextarea>(null);
|
||||
const editorStateRef = useRef<string>(null);
|
||||
|
||||
const handleChange: React.ChangeEventHandler<HTMLTextAreaElement> = (e) => {
|
||||
dispatch(changeCompose(id, e.target.value));
|
||||
};
|
||||
|
||||
const handleKeyDown: React.KeyboardEventHandler = (e) => {
|
||||
if (e.keyCode === 13 && (e.ctrlKey || e.metaKey)) {
|
||||
handleSubmit();
|
||||
e.preventDefault(); // Prevent bubbling to other ComposeForm instances
|
||||
}
|
||||
};
|
||||
|
||||
const getClickableArea = () => {
|
||||
return clickableAreaRef ? clickableAreaRef.current : formRef.current;
|
||||
};
|
||||
|
@ -139,11 +124,6 @@ const ComposeForm = <ID extends string>({ id, shouldCondense, autoFocus, clickab
|
|||
|
||||
const handleSubmit = (e?: React.FormEvent<Element>) => {
|
||||
dispatch(changeCompose(id, editorStateRef.current!));
|
||||
// if (text !== autosuggestTextareaRef.current?.textarea?.value) {
|
||||
// // Something changed the text inside the textarea (e.g. browser extensions like Grammarly)
|
||||
// // Update the state to match the current text
|
||||
// dispatch(changeCompose(id, autosuggestTextareaRef.current!.textarea!.value));
|
||||
// }
|
||||
|
||||
// Submit disabled:
|
||||
const fulltext = [spoilerText, countableText(text)].join('');
|
||||
|
@ -167,10 +147,6 @@ const ComposeForm = <ID extends string>({ id, shouldCondense, autoFocus, clickab
|
|||
dispatch(fetchComposeSuggestions(id, token as string));
|
||||
};
|
||||
|
||||
const onSuggestionSelected = (tokenStart: number, token: string | null, value: string | undefined) => {
|
||||
if (value) dispatch(selectComposeSuggestion(id, tokenStart, token, value, ['text']));
|
||||
};
|
||||
|
||||
const onSpoilerSuggestionSelected = (tokenStart: number, token: string | null, value: AutoSuggestion) => {
|
||||
dispatch(selectComposeSuggestion(id, tokenStart, token, value, ['spoiler_text']));
|
||||
};
|
||||
|
@ -245,7 +221,6 @@ const ComposeForm = <ID extends string>({ id, shouldCondense, autoFocus, clickab
|
|||
|
||||
let publishText: string | JSX.Element = '';
|
||||
let publishIcon: string | undefined = undefined;
|
||||
let textareaPlaceholder: MessageDescriptor;
|
||||
|
||||
if (isEditing) {
|
||||
publishText = intl.formatMessage(messages.saveChanges);
|
||||
|
@ -263,14 +238,6 @@ const ComposeForm = <ID extends string>({ id, shouldCondense, autoFocus, clickab
|
|||
publishText = intl.formatMessage(messages.schedule);
|
||||
}
|
||||
|
||||
if (event) {
|
||||
textareaPlaceholder = messages.eventPlaceholder;
|
||||
} else if (hasPoll) {
|
||||
textareaPlaceholder = messages.pollPlaceholder;
|
||||
} else {
|
||||
textareaPlaceholder = messages.placeholder;
|
||||
}
|
||||
|
||||
return (
|
||||
<Stack className='w-full' space={4} ref={formRef} onClick={handleClick} element='form' onSubmit={handleSubmit}>
|
||||
{scheduledStatusCount > 0 && !event && !group && (
|
||||
|
@ -301,10 +268,14 @@ const ComposeForm = <ID extends string>({ id, shouldCondense, autoFocus, clickab
|
|||
<div>
|
||||
<ComposeEditor
|
||||
ref={editorStateRef}
|
||||
condensed={condensed}
|
||||
onFocus={handleComposeFocus}
|
||||
autoFocus={shouldAutoFocus}
|
||||
className='my-2'
|
||||
composeId={id}
|
||||
condensed={condensed}
|
||||
eventDiscussion={!!event}
|
||||
autoFocus={shouldAutoFocus}
|
||||
hasPoll={hasPoll}
|
||||
onFocus={handleComposeFocus}
|
||||
onPaste={onPaste}
|
||||
/>
|
||||
{!condensed && (
|
||||
<Stack space={4} className='compose-form__modifiers'>
|
||||
|
@ -323,26 +294,6 @@ const ComposeForm = <ID extends string>({ id, shouldCondense, autoFocus, clickab
|
|||
)}
|
||||
</div>
|
||||
|
||||
<AutosuggestTextarea
|
||||
ref={(isModalOpen && shouldCondense) ? undefined : autosuggestTextareaRef}
|
||||
placeholder={intl.formatMessage(textareaPlaceholder)}
|
||||
disabled={disabled}
|
||||
value={text}
|
||||
onChange={handleChange}
|
||||
suggestions={suggestions}
|
||||
onKeyDown={handleKeyDown}
|
||||
onFocus={handleComposeFocus}
|
||||
onSuggestionsFetchRequested={onSuggestionsFetchRequested}
|
||||
onSuggestionsClearRequested={onSuggestionsClearRequested}
|
||||
onSuggestionSelected={onSuggestionSelected}
|
||||
onPaste={onPaste}
|
||||
autoFocus={shouldAutoFocus}
|
||||
condensed={condensed}
|
||||
id='compose-textarea'
|
||||
>
|
||||
<></>
|
||||
</AutosuggestTextarea>
|
||||
|
||||
<QuotedStatusContainer composeId={id} />
|
||||
|
||||
{extra && <div className={clsx({ 'hidden': condensed })}>{extra}</div>}
|
||||
|
|
|
@ -46,7 +46,29 @@ const StatePlugin = ({ composeId }: { composeId: string }) => {
|
|||
return null;
|
||||
};
|
||||
|
||||
const ComposeEditor = React.forwardRef<string, any>(({ composeId, condensed, onFocus, autoFocus }, editorStateRef) => {
|
||||
interface IComposeEditor {
|
||||
className?: string
|
||||
composeId: string
|
||||
condensed?: boolean
|
||||
eventDiscussion?: boolean
|
||||
hasPoll?: boolean
|
||||
autoFocus?: boolean
|
||||
onFocus?: React.FocusEventHandler<HTMLDivElement>
|
||||
onPaste?: (files: FileList) => void
|
||||
placeholder?: JSX.Element | string
|
||||
}
|
||||
|
||||
const ComposeEditor = React.forwardRef<string, IComposeEditor>(({
|
||||
className,
|
||||
composeId,
|
||||
condensed,
|
||||
eventDiscussion,
|
||||
hasPoll,
|
||||
autoFocus,
|
||||
onFocus,
|
||||
onPaste,
|
||||
placeholder,
|
||||
}, editorStateRef) => {
|
||||
const dispatch = useAppDispatch();
|
||||
const features = useFeatures();
|
||||
|
||||
|
@ -111,14 +133,29 @@ const ComposeEditor = React.forwardRef<string, any>(({ composeId, condensed, onF
|
|||
}
|
||||
};
|
||||
|
||||
const handlePaste: React.ClipboardEventHandler<HTMLDivElement> = (e) => {
|
||||
if (onPaste && e.clipboardData && e.clipboardData.files.length === 1) {
|
||||
onPaste(e.clipboardData.files);
|
||||
e.preventDefault();
|
||||
}
|
||||
};
|
||||
|
||||
let textareaPlaceholder = placeholder || <FormattedMessage id='compose_form.placeholder' defaultMessage="What's on your mind?" />;
|
||||
|
||||
if (eventDiscussion) {
|
||||
textareaPlaceholder = <FormattedMessage id='compose_form.event_placeholder' defaultMessage='Post to this event' />;
|
||||
} else if (hasPoll) {
|
||||
textareaPlaceholder = <FormattedMessage id='compose_form.poll_placeholder' defaultMessage='Add a poll topic…' />;
|
||||
}
|
||||
|
||||
return (
|
||||
<LexicalComposer initialConfig={initialConfig}>
|
||||
<div className='lexical relative' data-markup>
|
||||
<div className={clsx('lexical relative', className)} data-markup>
|
||||
<RichTextPlugin
|
||||
contentEditable={
|
||||
<div className='editor' ref={onRef} onFocus={onFocus}>
|
||||
<div className='editor' ref={onRef} onFocus={onFocus} onPaste={handlePaste}>
|
||||
<ContentEditable
|
||||
className={clsx('mr-4 py-2 outline-none transition-[min-height] motion-reduce:transition-none', {
|
||||
className={clsx('mr-4 outline-none transition-[min-height] motion-reduce:transition-none', {
|
||||
'min-fh-[40px]': condensed,
|
||||
'min-h-[100px]': !condensed,
|
||||
})}
|
||||
|
@ -127,8 +164,10 @@ const ComposeEditor = React.forwardRef<string, any>(({ composeId, condensed, onF
|
|||
</div>
|
||||
}
|
||||
placeholder={(
|
||||
<div className='pointer-events-none absolute top-2 select-none text-gray-600 dark:placeholder:text-gray-600'>
|
||||
<FormattedMessage id='compose_form.placeholder' defaultMessage="What's on your mind" />
|
||||
<div
|
||||
className='pointer-events-none absolute top-0 select-none text-gray-600 dark:placeholder:text-gray-600'
|
||||
>
|
||||
{textareaPlaceholder}
|
||||
</div>
|
||||
)}
|
||||
ErrorBoundary={LexicalErrorBoundary}
|
||||
|
|
|
@ -51,7 +51,7 @@ export type MenuRenderFn = (
|
|||
anchorElementRef: MutableRefObject<HTMLElement | null>,
|
||||
) => ReactPortal | JSX.Element | null;
|
||||
|
||||
function tryToPositionRange(leadOffset: number, range: Range): boolean {
|
||||
const tryToPositionRange = (leadOffset: number, range: Range): boolean => {
|
||||
const domSelection = window.getSelection();
|
||||
if (domSelection === null || !domSelection.isCollapsed) {
|
||||
return false;
|
||||
|
@ -72,12 +72,12 @@ function tryToPositionRange(leadOffset: number, range: Range): boolean {
|
|||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
function isSelectionOnEntityBoundary(
|
||||
const isSelectionOnEntityBoundary = (
|
||||
editor: LexicalEditor,
|
||||
offset: number,
|
||||
): boolean {
|
||||
): boolean => {
|
||||
if (offset !== 0) {
|
||||
return false;
|
||||
}
|
||||
|
@ -91,21 +91,21 @@ function isSelectionOnEntityBoundary(
|
|||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
function startTransition(callback: () => void) {
|
||||
const startTransition = (callback: () => void) => {
|
||||
if (React.startTransition) {
|
||||
React.startTransition(callback);
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Got from https://stackoverflow.com/a/42543908/2013580
|
||||
export function getScrollParent(
|
||||
export const getScrollParent = (
|
||||
element: HTMLElement,
|
||||
includeHidden: boolean,
|
||||
): HTMLElement | HTMLBodyElement {
|
||||
): HTMLElement | HTMLBodyElement => {
|
||||
let style = getComputedStyle(element);
|
||||
const excludeStaticParent = style.position === 'absolute';
|
||||
const overflowRegex = includeHidden
|
||||
|
@ -130,24 +130,24 @@ export function getScrollParent(
|
|||
}
|
||||
}
|
||||
return document.body;
|
||||
}
|
||||
};
|
||||
|
||||
function isTriggerVisibleInNearestScrollContainer(
|
||||
const isTriggerVisibleInNearestScrollContainer = (
|
||||
targetElement: HTMLElement,
|
||||
containerElement: HTMLElement,
|
||||
): boolean {
|
||||
): boolean => {
|
||||
const tRect = targetElement.getBoundingClientRect();
|
||||
const cRect = containerElement.getBoundingClientRect();
|
||||
return tRect.top > cRect.top && tRect.top < cRect.bottom;
|
||||
}
|
||||
};
|
||||
|
||||
// Reposition the menu on scroll, window resize, and element resize.
|
||||
export function useDynamicPositioning(
|
||||
export const useDynamicPositioning = (
|
||||
resolution: Resolution | null,
|
||||
targetElement: HTMLElement | null,
|
||||
onReposition: () => void,
|
||||
onVisibilityChange?: (isInView: boolean) => void,
|
||||
) {
|
||||
) => {
|
||||
const [editor] = useLexicalComposerContext();
|
||||
useEffect(() => {
|
||||
if (targetElement && resolution) {
|
||||
|
@ -161,9 +161,9 @@ export function useDynamicPositioning(
|
|||
targetElement,
|
||||
rootScrollParent,
|
||||
);
|
||||
const handleScroll = function () {
|
||||
const handleScroll = () => {
|
||||
if (!ticking) {
|
||||
window.requestAnimationFrame(function () {
|
||||
window.requestAnimationFrame(() => {
|
||||
onReposition();
|
||||
ticking = false;
|
||||
});
|
||||
|
@ -194,22 +194,17 @@ export function useDynamicPositioning(
|
|||
};
|
||||
}
|
||||
}, [targetElement, editor, onVisibilityChange, onReposition, resolution]);
|
||||
}
|
||||
};
|
||||
|
||||
function LexicalPopoverMenu({
|
||||
anchorElementRef,
|
||||
menuRenderFn,
|
||||
}: {
|
||||
const LexicalPopoverMenu = ({ anchorElementRef, menuRenderFn }: {
|
||||
anchorElementRef: MutableRefObject<HTMLElement>
|
||||
menuRenderFn: MenuRenderFn
|
||||
}): JSX.Element | null {
|
||||
return menuRenderFn(anchorElementRef);
|
||||
}
|
||||
}): JSX.Element | null => menuRenderFn(anchorElementRef);
|
||||
|
||||
function useMenuAnchorRef(
|
||||
const useMenuAnchorRef = (
|
||||
resolution: Resolution | null,
|
||||
setResolution: (r: Resolution | null) => void,
|
||||
): MutableRefObject<HTMLElement> {
|
||||
): MutableRefObject<HTMLElement> => {
|
||||
const [editor] = useLexicalComposerContext();
|
||||
const anchorElementRef = useRef<HTMLElement>(document.createElement('div'));
|
||||
const positionMenu = useCallback(() => {
|
||||
|
@ -272,7 +267,7 @@ function useMenuAnchorRef(
|
|||
);
|
||||
|
||||
return anchorElementRef;
|
||||
}
|
||||
};
|
||||
|
||||
export type AutosuggestPluginProps = {
|
||||
composeId: string
|
||||
|
@ -280,11 +275,11 @@ export type AutosuggestPluginProps = {
|
|||
setSuggestionsHidden: (value: boolean) => void
|
||||
};
|
||||
|
||||
export function AutosuggestPlugin({
|
||||
export const AutosuggestPlugin = ({
|
||||
composeId,
|
||||
suggestionsHidden,
|
||||
setSuggestionsHidden,
|
||||
}: AutosuggestPluginProps): JSX.Element | null {
|
||||
}: AutosuggestPluginProps): JSX.Element | null => {
|
||||
const { suggestions } = useCompose(composeId);
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
|
@ -473,4 +468,4 @@ export function AutosuggestPlugin({
|
|||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -12,7 +12,6 @@ import { useCallback, useEffect } from 'react';
|
|||
|
||||
import { $createMentionNode, MentionNode } from '../nodes/mention-node';
|
||||
|
||||
|
||||
import type { TextNode } from 'lexical';
|
||||
|
||||
export const MENTION_REGEX = new RegExp('(^|$|(?:^|\\s))([@])([a-z\\d_-]+(?:@[^@\\s]+)?)', 'i');
|
||||
|
|
|
@ -75,5 +75,4 @@ const EmojiPickerDropdownContainer = (
|
|||
);
|
||||
};
|
||||
|
||||
|
||||
export default EmojiPickerDropdownContainer;
|
||||
|
|
|
@ -11,7 +11,6 @@ const messages = defineMessages({
|
|||
|
||||
const listenerOptions = supportsPassiveEvents ? { passive: true } : false;
|
||||
|
||||
|
||||
interface IIconPickerMenu {
|
||||
icons: Record<string, Array<string>>
|
||||
onClose: () => void
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useEffect, useState } from 'react';
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
|
||||
|
||||
import {
|
||||
|
@ -21,9 +21,10 @@ import { closeModal, openModal } from 'soapbox/actions/modals';
|
|||
import { ADDRESS_ICONS } from 'soapbox/components/autosuggest-location';
|
||||
import LocationSearch from 'soapbox/components/location-search';
|
||||
import { checkEventComposeContent } from 'soapbox/components/modal-root';
|
||||
import { Button, Form, FormGroup, HStack, Icon, IconButton, Input, Modal, Spinner, Stack, Tabs, Text, Textarea, Toggle } from 'soapbox/components/ui';
|
||||
import { Button, Form, FormGroup, HStack, Icon, IconButton, Input, Modal, Spinner, Stack, Tabs, Text, Toggle } from 'soapbox/components/ui';
|
||||
import AccountContainer from 'soapbox/containers/account-container';
|
||||
import { isCurrentOrFutureDate } from 'soapbox/features/compose/components/schedule-form';
|
||||
import ComposeEditor from 'soapbox/features/compose/editor';
|
||||
import BundleContainer from 'soapbox/features/ui/containers/bundle-container';
|
||||
import { DatePicker } from 'soapbox/features/ui/util/async-components';
|
||||
import { useAppDispatch, useAppSelector } from 'soapbox/hooks';
|
||||
|
@ -94,13 +95,14 @@ const ComposeEventModal: React.FC<IComposeEventModal> = ({ onClose }) => {
|
|||
const intl = useIntl();
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const editorStateRef = useRef<string>(null);
|
||||
|
||||
const [tab, setTab] = useState<'edit' | 'pending'>('edit');
|
||||
|
||||
const banner = useAppSelector((state) => state.compose_event.banner);
|
||||
const isUploading = useAppSelector((state) => state.compose_event.is_uploading);
|
||||
|
||||
const name = useAppSelector((state) => state.compose_event.name);
|
||||
const description = useAppSelector((state) => state.compose_event.status);
|
||||
const startTime = useAppSelector((state) => state.compose_event.start_time);
|
||||
const endTime = useAppSelector((state) => state.compose_event.end_time);
|
||||
const approvalRequired = useAppSelector((state) => state.compose_event.approval_required);
|
||||
|
@ -114,10 +116,6 @@ const ComposeEventModal: React.FC<IComposeEventModal> = ({ onClose }) => {
|
|||
dispatch(changeEditEventName(target.value));
|
||||
};
|
||||
|
||||
const onChangeDescription: React.ChangeEventHandler<HTMLTextAreaElement> = ({ target }) => {
|
||||
dispatch(changeEditEventDescription(target.value));
|
||||
};
|
||||
|
||||
const onChangeStartTime = (date: Date) => {
|
||||
dispatch(changeEditEventStartTime(date));
|
||||
};
|
||||
|
@ -170,6 +168,7 @@ const ComposeEventModal: React.FC<IComposeEventModal> = ({ onClose }) => {
|
|||
};
|
||||
|
||||
const handleSubmit = () => {
|
||||
dispatch(changeEditEventDescription(editorStateRef.current!));
|
||||
dispatch(submitEvent());
|
||||
};
|
||||
|
||||
|
@ -238,11 +237,11 @@ const ComposeEventModal: React.FC<IComposeEventModal> = ({ onClose }) => {
|
|||
labelText={<FormattedMessage id='compose_event.fields.description_label' defaultMessage='Event description' />}
|
||||
hintText={<FormattedMessage id='compose_event.fields.description_hint' defaultMessage='Markdown syntax is supported' />}
|
||||
>
|
||||
<Textarea
|
||||
autoComplete='off'
|
||||
<ComposeEditor
|
||||
ref={editorStateRef}
|
||||
className='block w-full rounded-md border-gray-400 bg-white px-3 py-2 text-base text-gray-900 placeholder:text-gray-600 focus:border-primary-500 focus:ring-primary-500 dark:border-gray-800 dark:bg-gray-900 dark:text-gray-100 dark:ring-1 dark:ring-gray-800 dark:placeholder:text-gray-600 dark:focus:border-primary-500 dark:focus:ring-primary-500 sm:text-sm'
|
||||
composeId='compose-event-modal'
|
||||
placeholder={intl.formatMessage(messages.eventDescriptionPlaceholder)}
|
||||
value={description}
|
||||
onChange={onChangeDescription}
|
||||
/>
|
||||
</FormGroup>
|
||||
<FormGroup
|
||||
|
|
|
@ -135,7 +135,6 @@ const normalizeLocked = (group: ImmutableMap<string, any>) => {
|
|||
return group.set('locked', locked);
|
||||
};
|
||||
|
||||
|
||||
/** Rewrite `<p></p>` to empty string. */
|
||||
const fixNote = (group: ImmutableMap<string, any>) => {
|
||||
if (group.get('note') === '<p></p>') {
|
||||
|
|
|
@ -54,6 +54,7 @@ import {
|
|||
COMPOSE_EDITOR_STATE_SET,
|
||||
COMPOSE_SET_GROUP_TIMELINE_VISIBLE,
|
||||
} from '../actions/compose';
|
||||
import { EVENT_COMPOSE_CANCEL, EVENT_FORM_SET } from '../actions/events';
|
||||
import { ME_FETCH_SUCCESS, ME_PATCH_SUCCESS } from '../actions/me';
|
||||
import { SETTING_CHANGE, FE_NAME } from '../actions/settings';
|
||||
import { TIMELINE_DELETE } from '../actions/timelines';
|
||||
|
@ -507,6 +508,10 @@ export default function compose(state = initialState, action: AnyAction) {
|
|||
return updateCompose(state, 'default', compose => updateSetting(compose, action.path, action.value));
|
||||
case COMPOSE_EDITOR_STATE_SET:
|
||||
return updateCompose(state, action.id, compose => compose.set('editorState', action.editorState));
|
||||
case EVENT_COMPOSE_CANCEL:
|
||||
return updateCompose(state, 'event-compose-modal', compose => compose.set('text', ''));
|
||||
case EVENT_FORM_SET:
|
||||
return updateCompose(state, 'event-compose-modal', compose => compose.set('text', action.text));
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
|
|
@ -24,7 +24,6 @@ const getScopes = (state: RootState) => {
|
|||
return getInstanceScopes(state.instance);
|
||||
};
|
||||
|
||||
|
||||
export {
|
||||
getInstanceScopes,
|
||||
getScopes,
|
||||
|
|
Loading…
Reference in a new issue