diff --git a/app/soapbox/components/modal-root.tsx b/app/soapbox/components/modal-root.tsx index f3cdac7da..aded268e9 100644 --- a/app/soapbox/components/modal-root.tsx +++ b/app/soapbox/components/modal-root.tsx @@ -5,15 +5,18 @@ import { FormattedMessage, defineMessages, useIntl } from 'react-intl'; import { useHistory } from 'react-router-dom'; import { cancelReplyCompose } from 'soapbox/actions/compose'; +import { cancelEventCompose } from 'soapbox/actions/events'; import { openModal, closeModal } from 'soapbox/actions/modals'; -import { useAppDispatch, useAppSelector, usePrevious } from 'soapbox/hooks'; +import { useAppDispatch, usePrevious } from 'soapbox/hooks'; import type { UnregisterCallback } from 'history'; import type { ModalType } from 'soapbox/features/ui/components/modal-root'; import type { ReducerCompose } from 'soapbox/reducers/compose'; +import type { ReducerRecord as ReducerComposeEvent } from 'soapbox/reducers/compose-event'; const messages = defineMessages({ confirm: { id: 'confirmations.delete.confirm', defaultMessage: 'Delete' }, + cancelEditing: { id: 'confirmations.cancel_editing.confirm', defaultMessage: 'Cancel editing' }, }); export const checkComposeContent = (compose?: ReturnType) => { @@ -25,6 +28,15 @@ export const checkComposeContent = (compose?: ReturnType) ].some(check => check === true); }; +export const checkEventComposeContent = (compose?: ReturnType) => { + return !!compose && [ + compose.name.length > 0, + compose.status.length > 0, + compose.location !== null, + compose.banner !== null, + ].some(check => check === true); +}; + interface IModalRoot { onCancel?: () => void, onClose: (type?: ModalType) => void, @@ -46,8 +58,6 @@ const ModalRoot: React.FC = ({ children, onCancel, onClose, type }) const prevChildren = usePrevious(children); const prevType = usePrevious(type); - const isEditing = useAppSelector(state => state.compose.get('compose-modal')?.id !== null); - const visible = !!children; const handleKeyUp = (e: KeyboardEvent) => { @@ -58,13 +68,20 @@ const ModalRoot: React.FC = ({ children, onCancel, onClose, type }) const handleOnClose = () => { dispatch((_, getState) => { - const hasComposeContent = checkComposeContent(getState().compose.get('compose-modal')); + const compose = getState().compose.get('compose-modal'); + const hasComposeContent = checkComposeContent(compose); + const hasEventComposeContent = checkEventComposeContent(getState().compose_event); if (hasComposeContent && type === 'COMPOSE') { + const isEditing = compose!.id !== null; dispatch(openModal('CONFIRM', { icon: require('@tabler/icons/trash.svg'), - heading: isEditing ? : , - message: isEditing ? : , + heading: isEditing + ? + : , + message: isEditing + ? + : , confirm: intl.formatMessage(messages.confirm), onConfirm: () => { dispatch(closeModal('COMPOSE')); @@ -74,7 +91,26 @@ const ModalRoot: React.FC = ({ children, onCancel, onClose, type }) dispatch(closeModal('CONFIRM')); }, })); - } else if (hasComposeContent && type === 'CONFIRM') { + } else if (hasEventComposeContent && type === 'COMPOSE_EVENT') { + const isEditing = getState().compose_event.id !== null; + dispatch(openModal('CONFIRM', { + icon: require('@tabler/icons/trash.svg'), + heading: isEditing + ? + : , + message: isEditing + ? + : , + confirm: intl.formatMessage(isEditing ? messages.cancelEditing : messages.confirm), + onConfirm: () => { + dispatch(closeModal('COMPOSE_EVENT')); + dispatch(cancelEventCompose()); + }, + onCancel: () => { + dispatch(closeModal('CONFIRM')); + }, + })); + } else if ((hasComposeContent || hasEventComposeContent) && type === 'CONFIRM') { dispatch(closeModal('CONFIRM')); } else { onClose(); diff --git a/app/soapbox/features/event/event-information.tsx b/app/soapbox/features/event/event-information.tsx index 54c5f5f40..7e6f2d4a3 100644 --- a/app/soapbox/features/event/event-information.tsx +++ b/app/soapbox/features/event/event-information.tsx @@ -56,9 +56,9 @@ const EventInformation: React.FC = ({ params }) => { }; const renderEventLocation = useCallback(() => { - const event = status?.event; + const event = status!.event!; - return event?.location && ( + return event.location && ( @@ -86,12 +86,12 @@ const EventInformation: React.FC = ({ params }) => { }, [status]); const renderEventDate = useCallback(() => { - const event = status?.event; + const event = status!.event!; + + if (!event.start_time) return null; const startDate = new Date(event.start_time); - const endDate = new Date(event.end_time); - - if (!startDate) return null; + const endDate = event.end_time && new Date(event.end_time); const sameDay = endDate && startDate.getDate() === endDate.getDate() && startDate.getMonth() === endDate.getMonth() && startDate.getFullYear() === endDate.getFullYear(); diff --git a/app/soapbox/features/ui/components/modals/compose-event-modal/compose-event-modal.tsx b/app/soapbox/features/ui/components/modals/compose-event-modal/compose-event-modal.tsx index 4bbe709e2..ca041a758 100644 --- a/app/soapbox/features/ui/components/modals/compose-event-modal/compose-event-modal.tsx +++ b/app/soapbox/features/ui/components/modals/compose-event-modal/compose-event-modal.tsx @@ -16,9 +16,12 @@ import { fetchEventParticipationRequests, rejectEventParticipationRequest, authorizeEventParticipationRequest, + cancelEventCompose, } from 'soapbox/actions/events'; +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 } from 'soapbox/components/ui'; import AccountContainer from 'soapbox/containers/account-container'; import { isCurrentOrFutureDate } from 'soapbox/features/compose/components/schedule-form'; @@ -38,6 +41,8 @@ const messages = defineMessages({ pending: { id: 'compose_event.tabs.pending', defaultMessage: 'Manage requests' }, authorize: { id: 'compose_event.participation_requests.authorize', defaultMessage: 'Authorize' }, reject: { id: 'compose_event.participation_requests.reject', defaultMessage: 'Reject' }, + confirm: { id: 'confirmations.delete.confirm', defaultMessage: 'Delete' }, + cancelEditing: { id: 'confirmations.cancel_editing.confirm', defaultMessage: 'Cancel editing' }, }); @@ -136,7 +141,26 @@ const ComposeEventModal: React.FC = ({ onClose }) => { }; const onClickClose = () => { - onClose('COMPOSE_EVENT'); + dispatch((dispatch, getState) => { + if (checkEventComposeContent(getState().compose_event)) { + dispatch(openModal('CONFIRM', { + icon: require('@tabler/icons/trash.svg'), + heading: id + ? + : , + message: id + ? + : , + confirm: intl.formatMessage(messages.confirm), + onConfirm: () => { + dispatch(closeModal('COMPOSE_EVENT')); + dispatch(cancelEventCompose()); + }, + })); + } else { + onClose('COMPOSE_EVENT'); + } + }); }; const handleFiles = (files: FileList) => { diff --git a/app/soapbox/features/ui/index.tsx b/app/soapbox/features/ui/index.tsx index 701567ccb..8601deaf7 100644 --- a/app/soapbox/features/ui/index.tsx +++ b/app/soapbox/features/ui/index.tsx @@ -120,6 +120,7 @@ import { WrappedRoute } from './util/react-router-helpers'; // Dummy import, to make sure that ends up in the application bundle. // Without this it ends up in ~8 very commonly used bundles. import 'soapbox/components/status'; +import { uploadEventBanner } from 'soapbox/actions/events'; const EmptyPage = HomePage; @@ -384,7 +385,9 @@ const UI: React.FC = ({ children }) => { if (e.dataTransfer && e.dataTransfer.files.length >= 1) { const modals = getState().modals; const isModalOpen = modals.last()?.modalType === 'COMPOSE'; - dispatch(uploadCompose(isModalOpen ? 'compose-modal' : 'home', e.dataTransfer.files, intl)); + const isEventsModalOpen = modals.last()?.modalType === 'COMPOSE_EVENT'; + if (isEventsModalOpen) dispatch(uploadEventBanner(e.dataTransfer.files[0], intl)); + else dispatch(uploadCompose(isModalOpen ? 'compose-modal' : 'home', e.dataTransfer.files, intl)); } }); }; diff --git a/app/soapbox/reducers/compose_event.ts b/app/soapbox/reducers/compose-event.ts similarity index 100% rename from app/soapbox/reducers/compose_event.ts rename to app/soapbox/reducers/compose-event.ts diff --git a/app/soapbox/reducers/index.ts b/app/soapbox/reducers/index.ts index c691aebaa..1556e658a 100644 --- a/app/soapbox/reducers/index.ts +++ b/app/soapbox/reducers/index.ts @@ -19,7 +19,7 @@ import chat_message_lists from './chat-message-lists'; import chat_messages from './chat-messages'; import chats from './chats'; import compose from './compose'; -import compose_event from './compose_event'; +import compose_event from './compose-event'; import contexts from './contexts'; import conversations from './conversations'; import custom_emojis from './custom-emojis';