From adc76ed79b74ccdcbe3ac25b9e5d9ef81504a22e Mon Sep 17 00:00:00 2001 From: Harshil Sharma <18575143+harshilsharma63@users.noreply.github.com> Date: Mon, 4 Nov 2024 12:31:45 +0530 Subject: [PATCH] Not scheduling post if inputs are open (#28964) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Create scheduled post api (#27920) * Added migration files for Postgres * Added migrations for MySQL * Added store method * Added API and store tests * Renamed migration after syncing with master * Added app layer tests * API is ready * API is ready * API is ready * Renamed migration after syncing with master * Updated migration list * Fixed retry layer tests * Allowed posts with empty messages * Review fixes * Reverted an incorrect change * Renamed migration and fixed ID assignment * CI * Send post button changes (#28019) * added Split button * WIP * Added core menu options * WIP * WIP * WIP * Handled displaying error in creating scheduled post * lint fixes * webapp i18n fix * Review fixes * Fixed a webapp test * A few more fixes * Removed a duplicate comment * Scheduled post job (#28088) * Added the job function * Added query for fetching scheduled posts for pricessing * WIP * WIP * WIP * WIP * WIP * WIP * Reafactoring of scheduled post job * Lint fixes * Updated i18n files * FInishing touches * Added tests for GetScheduledPosts * Added tests for PermanentlyDeleteScheduledPosts * Updated all layer * Some changes as discussed with team * Added tests for UpdatedScheduledPost * Code review refactoring * Added job test * MM-60120 - Custom time selection (#28120) * Added a common date time picker modal and used it for post reminder * Added a common date time picker modal and used it for post reminderggp * Added modal for custom schedule time and fixed TZ issue * WIP * Removed event from useSubmit hook * Removed event from useSubmit hook * Added timezone handling * fixed type error * Updated i18n strings * Minor cleanup * updated snapshots * review fixes * Handled event * Supported for having a DM thread open in RHS while in a regular channel * Review fixes * MM-60136 - Scheduled messages tab (#28133) * WIP * WIP * Created Tabs and Tab wrapper with added styling * Added API to get scheduled posts * WIP * Displated scheduled post count * i18n fix * Added tests * Handled asetting active tab absed on URL: * Reverted unintended change * Added API to client ad OpenAPI specs * Renamed file * Adding fileinfo to schedule posts * Partial review fixes * Made get scheduled post API return posts by teamID * review fixes * Moved scheduled post redux code to MM-redux package * Usedd selector factory * WIP: * WIP: * Lint fix * Fixed an incorrect openapi spec file * Removed redundent permission check * Clreaed scheduled post data on logout * Removed unused i18n string: * lint fix * Render scheduled posts (#28208) * WIP * WIP * Created Tabs and Tab wrapper with added styling * Added API to get scheduled posts * WIP * Displated scheduled post count * i18n fix * Added tests * Handled asetting active tab absed on URL: * Reverted unintended change * Added API to client ad OpenAPI specs * Renamed file * Created common component for draft list item * WIP * WIP * Adding fileinfo to schedule posts * Basic rendering * Added count badge to tabs * WIP * Made the Drafts LHS iteam appear if no drafts exist but scheduled posts do * Fixed icon size * Partial review fixes * Made get scheduled post API return posts by teamID * Handled initial vs team switch load * Displayed scheduled date in panel header * Added error message and error indiocator * WIP * review fixes * WIP Adding error reason tag * Added error codes * Moved scheduled post redux code to MM-redux package * Usedd selector factory * WIP: * WIP: * Lint fix * Fixed an incorrect openapi spec file * Removed redundent permission check * Clreaed scheduled post data on logout * Removed unused i18n string: * lint fix * Opened rescheduling modal * Updated graphic for empty state of schduled post list * Added delete scheduled post option and modal * Badge and timezone fix * WIP: * Added send now confirmation modal * lint * Webapp i18n fix * Fixed webapp test * Fixed a bug where DM/GM scheduled posts weren't immideatly showing up in UI * Minor fixes * WIP * Review fixes * Review fixes * Optimisations * Fixed reducer name * Moment optimizatin * Updated route check * MM-60144 - added API to update a scheduled post (#28248) * WIP * Added api and ap layer for update scheduled post ̛̦̄ * Added API to OpenAI specs, Go client and TS client * removed permissio check * Added tests * Fixed tests * Added PreUpdate method on scheduled post model * MM-60131 - Reschedule post integration (#28281) * Handled rescheduling post in webapp * Added error handling * MM-60146 - Delete scheduled post api (#28265) * WIP * Added api and ap layer for update scheduled post ̛̦̄ * Added API to OpenAI specs, Go client and TS client * removed permissio check * Added tests * Fixed tests * Added PreUpdate method on scheduled post model * Added delete scheduled post API * Added API to Go client and OpenAPI specs * Added API to TS client * Added tests * CI * Rmeoved two incorrect code comments * MM-60653 - Integrated delete scheduled post API (#28296) * Integrated delete scheduled apost API * Lint fix * Review fixes * Excluded draft checks from scheduled posts (#28370) * Excluded draft checks from scheduled posts * Added a removed todo * MM-60125 - Scheduled post channel indicator (#28320) * Integrated delete scheduled apost API * Lint fix * Added state for storing scheduled posts by channel ID * Refactored redux store to store scheudled posts by ID, thens tore IDs everywhere * Refactored redux store to store scheudled posts by ID, thens tore IDs everywhere * WIP * Added scheduled post indiocator * Handled single and multiple scheudled posts * Review fixes * Fixed styling and handled center channel, RHS and threads view * Lint fix * i18n fix * Fixed a cycling dependency * Lint fix * Added some more comments * Updated styling * Review fixes * Added common component for remote user time and scheduled post indicator * Updated scheduled post count * Minor change * Moved CSS code around * Fixed a bug where files in scheduled post didn't show up until refresh (#28359) --------- Co-authored-by: Daniel Espino García * Scheduled post config (#28485) * Added config * Added config on server and webapp side * Added config check in server and webapp * Added license check * Added license check * Added placeholder help text * Added license check to job * Fixed job test * Review fixes * Updated English text * Review fixes * MM-60118 - Added index on ScheduledPosts table (#28579) * Added index * Updated indexes * Scheduled posts misc fixes (#28625) * Added detailed logging for scheduled post job * Limited scheduled posts processing to 24 hours * Marked old scheduled posts as unable to send * Added t5ests * converted some logs to trace level * Fixed a bug causing error message to show up on deleting a scheduled post in a deleted thread (#28630) * Fixed scheduled posts link in RHS (#28659) * Fixed scheduled posts link in RHS * Review fixes * Fix permission name in scheduled posts by team (#28580) * Fix permission name * fix wording --------- Co-authored-by: Mattermost Build * FIxed width of generic modal header to fix browser channel modal (#28639) * Only consider error-free scheduled posts for indicator in channel and RHS (#28683) * Show only errro free scheudled posts in post box indicator * Fixed a bug to handle no scheduled posts * Fixed draft and scheudled post UI in mobile view (#28680) * MM-60873 and MM-60872 - Fixed a bug with updating scheduled posts (#28656) * Fixed a bug with updating scheduled posts * Better selectors * MOved shceuled post message length validation to app layer * MM-60732 - Scheduled posts channel link now takes you to the first scheduled post in channel/thread in list (#28768) * Ordered scheudle dposts by schgeudled at nad create at * Ordered in client * Added scroll to target * Removed classname prop * Fixed tests * Added doc * Import fix * MM-60961 - Fixed a bug where API used incoming create at date for scheduled post (#28703) * Fixed a bug where API used incoming create at date for scheduled post * Stopped sending created at value for scheduled post * MM-60785 - Fixed a bug where scheduled posts of channel we are no longer member of didn't show up (#28637) * Fixed a bug where scheduled posts of channel we are no longer member of didn't show up * Added a comment * CI * Used data loader to optimise laoding missing channels * Minor refactoring * MM-60963 - Added common checks for post and scheduled posts (#28713) * Added commen checks for post and scheuled posts * Sanitised scheduled posts * Fixed tests * Splitted post checks into app and context functions * Added checks on scheduiled posts job as well: * i18n fix * Fixed a test * Renamed a func * removed duplicate check * Scheduled posts UI fixes (#28828) * Fixed send button and time picker borders * Fixed center alignment of time picker * Removed on for today and tomorrow * Lint fix * Date time modal hover state fix * Badge fix * Fixed a mnerge issue * Scheduled Post send now and add schedule on draft (#28851) * Added send now option on scheduled posts * Minor refactoring * WIP * WIP * WIP * Lint fix * i18n fix * Snapshot update * Review fixes * Scheduled post inline editing (#28893) * Added send now option on scheduled posts * Minor refactoring * WIP * WIP * WIP * Lint fix * i18n fix * Snapshot update * Displayed editing component in scheduled post * Added handling for updating scheduled post * Handle events * Fixed escape key issue in scheudled post editing * Fixes * Displayed error message for editing error * Don't show mention warning * Handled dev mode (#28918) * Not scheduling post if inputs are open * MInor fixes * Lint fix * client fix * Fixes * CI * Removed dev mode behaviour temperorily (#29008) --------- Co-authored-by: Daniel Espino García Co-authored-by: Eva Sarafianou Co-authored-by: Mattermost Build --- .../custom_status/custom_status_modal.tsx | 24 ++++++++++++----- .../custom_status/date_time_input.tsx | 27 ++++++++++++------- .../date_time_picker_modal.tsx | 16 +++++++---- 3 files changed, 46 insertions(+), 21 deletions(-) diff --git a/webapp/channels/src/components/custom_status/custom_status_modal.tsx b/webapp/channels/src/components/custom_status/custom_status_modal.tsx index 8106c51484..8852a20c96 100644 --- a/webapp/channels/src/components/custom_status/custom_status_modal.tsx +++ b/webapp/channels/src/components/custom_status/custom_status_modal.tsx @@ -138,13 +138,13 @@ const CustomStatusModal: React.FC = (props: Props) => { initialCustomExpiryTime = moment(currentCustomStatus.expires_at); } const [customExpiryTime, setCustomExpiryTime] = useState(initialCustomExpiryTime); - const [isDatePickerOpen, setIsDatePickerOpen] = useState(false); + const [isInteracting, setIsInteracting] = useState(false); const handleKeyDown = useCallback((event: KeyboardEvent) => { - if (isKeyPressed(event, Constants.KeyCodes.ESCAPE) && !isDatePickerOpen) { + if (isKeyPressed(event, Constants.KeyCodes.ESCAPE) && !isInteracting) { props.onExited(); } - }, [isDatePickerOpen, props.onExited]); + }, [isInteracting, props.onExited]); useEffect(() => { document.addEventListener('keydown', handleKeyDown); @@ -180,9 +180,13 @@ const CustomStatusModal: React.FC = (props: Props) => { if (inCustomEmojiPath) { dispatch(closeModal(ModalIdentifiers.CUSTOM_STATUS)); } - }, [inCustomEmojiPath]); + }, [dispatch, inCustomEmojiPath]); const handleSetStatus = () => { + if (isInteracting) { + return; + } + const expiresAt = calculateExpiryTime(); const customStatus: UserCustomStatus = { emoji: emoji || 'speech_balloon', @@ -193,8 +197,15 @@ const CustomStatusModal: React.FC = (props: Props) => { customStatus.expires_at = expiresAt; } dispatch(setCustomStatus(customStatus)); + dispatch(closeModal(ModalIdentifiers.CUSTOM_STATUS)); }; + const handleEnterKeyPressed = useCallback(() => { + if (!isInteracting) { + handleSetStatus(); + } + }, [isInteracting, handleSetStatus]); + const calculateExpiryTime = (): string => { switch (duration) { case DONT_CLEAR: @@ -404,12 +415,13 @@ const CustomStatusModal: React.FC = (props: Props) => { id='custom_status_modal' className={'StatusModal'} handleConfirm={handleSetStatus} - handleEnterKeyPress={handleSetStatus} + handleEnterKeyPress={handleEnterKeyPressed} handleCancel={handleClearStatus} confirmButtonClassName='btn btn-primary' ariaLabel={formatMessage({id: 'custom_status.set_status', defaultMessage: 'Set a status'})} keyboardEscape={false} tabIndex={-1} + autoCloseOnConfirmButton={false} >
@@ -469,7 +481,7 @@ const CustomStatusModal: React.FC = (props: Props) => { time={customExpiryTime} handleChange={setCustomExpiryTime} timezone={timezone} - setIsDatePickerOpen={setIsDatePickerOpen} + setIsInteracting={setIsInteracting} /> )}
diff --git a/webapp/channels/src/components/custom_status/date_time_input.tsx b/webapp/channels/src/components/custom_status/date_time_input.tsx index 1584a36226..a1b680cd46 100644 --- a/webapp/channels/src/components/custom_status/date_time_input.tsx +++ b/webapp/channels/src/components/custom_status/date_time_input.tsx @@ -23,8 +23,8 @@ import Input from 'components/widgets/inputs/input/input'; import Menu from 'components/widgets/menu/menu'; import MenuWrapper from 'components/widgets/menu/menu_wrapper'; -import type {A11yFocusEventDetail} from 'utils/constants'; import Constants, {A11yCustomEventTypes} from 'utils/constants'; +import type {A11yFocusEventDetail} from 'utils/constants'; import {relativeFormatDate} from 'utils/datetime'; import {isKeyPressed} from 'utils/keyboard'; import {getCurrentMomentForTimezone} from 'utils/timezone'; @@ -66,14 +66,20 @@ type Props = { time: Moment; handleChange: (date: Moment) => void; timezone?: string; - setIsDatePickerOpen?: (isDatePickerOpen: boolean) => void; + setIsInteracting?: (interacting: boolean) => void; relativeDate?: boolean; timePickerInterval?: number; } -const DateTimeInputContainer: React.FC = (props: Props) => { +const DateTimeInputContainer: React.FC = ({ + time, + handleChange, + timezone, + setIsInteracting, + relativeDate, + timePickerInterval, +}: Props) => { const locale = useSelector(getCurrentLocale); - const {time, handleChange, timezone} = props; const [timeOptions, setTimeOptions] = useState([]); const [isPopperOpen, setIsPopperOpen] = useState(false); const {formatMessage} = useIntl(); @@ -82,8 +88,8 @@ const DateTimeInputContainer: React.FC = (props: Props) => { const handlePopperOpenState = useCallback((isOpen: boolean) => { setIsPopperOpen(isOpen); - props.setIsDatePickerOpen?.(isOpen); - }, []); + setIsInteracting?.(isOpen); + }, [setIsInteracting]); const handleKeyDown = useCallback((event: KeyboardEvent) => { if (isKeyPressed(event, Constants.KeyCodes.ESCAPE) && isPopperOpen) { @@ -103,9 +109,9 @@ const DateTimeInputContainer: React.FC = (props: Props) => { const currentTime = getCurrentMomentForTimezone(timezone); let startTime = moment(time).startOf('day'); if (currentTime.isSame(time, 'date')) { - startTime = getRoundedTime(currentTime, props.timePickerInterval); + startTime = getRoundedTime(currentTime, timePickerInterval); } - setTimeOptions(getTimeInIntervals(startTime, props.timePickerInterval)); + setTimeOptions(getTimeInIntervals(startTime, timePickerInterval)); }; useEffect(setTimeAndOptions, [time]); @@ -113,7 +119,7 @@ const DateTimeInputContainer: React.FC = (props: Props) => { const handleDayChange = (day: Date, modifiers: DayModifiers) => { if (modifiers.today) { const currentTime = getCurrentMomentForTimezone(timezone); - const roundedTime = getRoundedTime(currentTime, props.timePickerInterval); + const roundedTime = getRoundedTime(currentTime, timePickerInterval); handleChange(roundedTime); } else { const dayWithTimezone = timezone ? moment(day).tz(timezone, true) : moment(day); @@ -142,7 +148,7 @@ const DateTimeInputContainer: React.FC = (props: Props) => { }, []); const formatDate = (date: Moment): string => { - return props.relativeDate ? relativeFormatDate(date, formatMessage, DATE_FORMAT) : DateTime.fromJSDate(date.toDate()).toFormat(DATE_FORMAT); + return relativeDate ? relativeFormatDate(date, formatMessage, DATE_FORMAT) : DateTime.fromJSDate(date.toDate()).toFormat(DATE_FORMAT); }; const inputIcon = ( @@ -192,6 +198,7 @@ const DateTimeInputContainer: React.FC = (props: Props) => {