Skip to content

Commit

Permalink
Error handled if API call fails for scheduled post
Browse files Browse the repository at this point in the history
  • Loading branch information
Rajat-Dabade committed Feb 25, 2025
1 parent d6f6e0e commit d53d2d5
Show file tree
Hide file tree
Showing 7 changed files with 98 additions and 18 deletions.
2 changes: 1 addition & 1 deletion app/actions/remote/scheduled_post.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ describe('updateScheduledPost', () => {
const spyHandleScheduledPosts = jest.spyOn(operator, 'handleScheduledPosts');
await operator.handleUsers({users: [user1], prepareRecordsOnly: false});
const mockResponse = {...scheduledPost, update_at: Date.now()};
mockClient.updateScheduledPost.mockImplementationOnce(() => mockResponse);
mockClient.updateScheduledPost.mockImplementationOnce(() => Promise.resolve(mockResponse));
const result = await updateScheduledPost(serverUrl, scheduledPost, undefined, true);
expect(result.error).toBeUndefined();
expect(result.scheduledPost).toEqual(mockResponse);
Expand Down
2 changes: 2 additions & 0 deletions app/constants/snack_bar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ export const SNACK_BAR_TYPE = keyMirror({
UNMUTE_CHANNEL: null,
UNFOLLOW_THREAD: null,
SCHEDULED_POST_CREATION_ERROR: null,
RESCHEDULED_POST: null,
DELETE_SCHEDULED_POST_ERROR: null,
});

export const MESSAGE_TYPE = {
Expand Down
27 changes: 27 additions & 0 deletions app/hooks/handle_send_message.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -622,4 +622,31 @@ describe('useHandleSendMessage', () => {
expect(createScheduledPost).toHaveBeenCalled();
});
});

describe('handle error while failing creating post from scheduled post and draft', () => {
it('should handle failed post creation', async () => {
jest.mocked(createPost).mockResolvedValueOnce({
error: new Error('Failed to create post'),
});

jest.mock('@utils/snack_bar', () => ({
showSnackBar: jest.fn(),
}));

const props = {
...defaultProps,
value: 'test message',
};

const {result} = renderHook(() => useHandleSendMessage(props), {wrapper});

await act(async () => {
const response = await result.current.handleSendMessage();
expect(response?.error).toBeDefined();
});

expect(defaultProps.clearDraft).toHaveBeenCalled();
expect(DeviceEventEmitter.emit).toHaveBeenCalledWith(Events.POST_LIST_SCROLL_TO_BOTTOM, Screens.CHANNEL);
});
});
});
15 changes: 13 additions & 2 deletions app/screens/draft_scheduled_post_options/send_draft.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,15 @@ import FormattedText from '@components/formatted_text';
import TouchableWithFeedback from '@components/touchable_with_feedback';
import {General} from '@constants';
import {ICON_SIZE} from '@constants/post_draft';
import {SNACK_BAR_TYPE} from '@constants/snack_bar';
import {useServerUrl} from '@context/server';
import {useTheme} from '@context/theme';
import {useHandleSendMessage} from '@hooks/handle_send_message';
import {usePersistentNotificationProps} from '@hooks/persistent_notification_props';
import {DRAFT_TYPE_DRAFT, type DraftType} from '@screens/global_drafts/constants';
import {dismissBottomSheet} from '@screens/navigation';
import {persistentNotificationsConfirmation, sendMessageWithAlert} from '@utils/post';
import {showSnackBar} from '@utils/snack_bar';
import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
import {typography} from '@utils/typography';

Expand Down Expand Up @@ -92,13 +94,22 @@ const SendDraft: React.FC<Props> = ({
const intl = useIntl();
const style = getStyleSheet(theme);
const serverUrl = useServerUrl();
const clearDraft = () => {

const clearDraft = async () => {
if (draftType === DRAFT_TYPE_DRAFT) {
removeDraft(serverUrl, channelId, rootId);
return;
}
if (postId) {
deleteScheduledPost(serverUrl, postId);
const res = await deleteScheduledPost(serverUrl, postId);
if (res?.error) {
showSnackBar({
barType: SNACK_BAR_TYPE.DELETE_SCHEDULED_POST_ERROR,
customMessage: (res.error as Error).message,
keepOpen: true,
type: 'error',
});
}
}
};

Expand Down
29 changes: 15 additions & 14 deletions app/screens/reschedule_draft/reschedule_draft.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,16 @@ import {Keyboard, SafeAreaView, StyleSheet, View} from 'react-native';

import {updateScheduledPost} from '@actions/remote/scheduled_post';
import Loading from '@components/loading';
import {SNACK_BAR_TYPE} from '@constants/snack_bar';
import {useServerUrl} from '@context/server';
import {useTheme} from '@context/theme';
import DatabaseManager from '@database/manager';
import useAndroidHardwareBackHandler from '@hooks/android_back_handler';
import useNavButtonPressed from '@hooks/navigation_button_pressed';
import DateTimeSelector from '@screens/custom_status_clear_after/components/date_time_selector';
import PostError from '@screens/edit_post/post_error';
import {buildNavigationButton, dismissModal, setButtons} from '@screens/navigation';
import {logDebug} from '@utils/log';
import {showSnackBar} from '@utils/snack_bar';
import {changeOpacity} from '@utils/theme';
import {getTimezone} from '@utils/user';

Expand Down Expand Up @@ -63,8 +64,6 @@ const RescheduledDraft: React.FC<Props> = ({
const {database} = DatabaseManager.getServerDatabaseAndOperator(serverUrl);
const mainView = useRef<View>(null);
const [isUpdating, setIsUpdating] = useState(false);
const [errorLine, setErrorLine] = useState<string | undefined>();
const [errorExtra, setErrorExtra] = useState<string | undefined>();
const selectedTime = useRef<string | null>(null);
const userTimezone = getTimezone(currentUserTimezone);

Expand Down Expand Up @@ -93,8 +92,13 @@ const RescheduledDraft: React.FC<Props> = ({
const handleUIUpdates = useCallback((res: {error?: unknown}) => {
if (res.error) {
setIsUpdating(false);
const errorMessage = intl.formatMessage({id: 'mobile.scheduled_post.update.error', defaultMessage: 'There was a problem editing this message. Please try again.'});
setErrorLine(errorMessage);
const errorMessage = intl.formatMessage({id: 'mobile.scheduled_post.update.error', defaultMessage: 'There was a problem updating this post message. Please try again.'});
showSnackBar({
barType: SNACK_BAR_TYPE.RESCHEDULED_POST,
customMessage: errorMessage,
keepOpen: true,
type: 'error',
});
} else {
setIsUpdating(false);
onClose();
Expand All @@ -103,14 +107,17 @@ const RescheduledDraft: React.FC<Props> = ({

const onSavePostMessage = useCallback(async () => {
setIsUpdating(true);
setErrorLine(undefined);
setErrorExtra(undefined);
toggleSaveButton(false);
if (!selectedTime.current) {
logDebug('ScheduledPostOptions', 'No time selected');
setIsUpdating(false);
const errorMessage = intl.formatMessage({id: 'mobile.scheduled_post.error', defaultMessage: 'No time selected'});
setErrorLine(errorMessage);
showSnackBar({
barType: SNACK_BAR_TYPE.RESCHEDULED_POST,
customMessage: errorMessage,
keepOpen: true,
type: 'error',
});
return;
}
const draftPayload = await draft.toApi(database);
Expand Down Expand Up @@ -148,12 +155,6 @@ const RescheduledDraft: React.FC<Props> = ({
style={styles.body}
ref={mainView}
>
{Boolean((errorLine || errorExtra)) &&
<PostError
errorExtra={errorExtra}
errorLine={errorLine}
/>
}
<View style={styles.optionsContainer}>
<DateTimeSelector
handleChange={handleCustomTimeChange}
Expand Down
12 changes: 11 additions & 1 deletion app/utils/scheduled_post/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import {defineMessages, type IntlShape} from 'react-intl';
import {Alert} from 'react-native';

import {deleteScheduledPost} from '@actions/remote/scheduled_post';
import {SNACK_BAR_TYPE} from '@constants/snack_bar';
import {showSnackBar} from '@utils/snack_bar';

import type {SwipeableMethods} from 'react-native-gesture-handler/lib/typescript/components/ReanimatedSwipeable';

Expand All @@ -20,7 +22,15 @@ export function deleteScheduledPostConfirmation({
swipeable?: React.RefObject<SwipeableMethods>;
}) {
const deleteScheduledPostOnConfirm = async () => {
await deleteScheduledPost(serverUrl, scheduledPostId);
const res = await deleteScheduledPost(serverUrl, scheduledPostId);
if (res?.error) {
showSnackBar({
barType: SNACK_BAR_TYPE.DELETE_SCHEDULED_POST_ERROR,
customMessage: (res.error as Error).message,
keepOpen: true,
type: 'error',
});
}
};

const onDismiss = () => {
Expand Down
29 changes: 29 additions & 0 deletions app/utils/scheduled_post/scheduled_post.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ import {deleteScheduledPostConfirmation, getErrorStringFromCode, type ScheduledP
import type {IntlShape} from 'react-intl';
import type {SwipeableMethods} from 'react-native-gesture-handler/lib/typescript/components/ReanimatedSwipeable';

// Mock dependencies before importing the module under test
jest.mock('@utils/snack_bar', () => ({
showSnackBar: jest.fn(),
}));

jest.mock('@actions/remote/scheduled_post', () => ({
deleteScheduledPost: jest.fn(),
}));
Expand All @@ -17,6 +22,9 @@ jest.mock('react-native', () => ({
Alert: {
alert: jest.fn(),
},
Platform: {
select: jest.fn((obj) => obj.ios || obj.default),
},
}));

describe('deleteScheduledPostConfirmation', () => {
Expand Down Expand Up @@ -89,6 +97,27 @@ describe('deleteScheduledPostConfirmation', () => {
// Should not throw when swipeable is undefined
expect(() => cancelButton.onPress()).not.toThrow();
});

it('shows error snackbar when deleteScheduledPost fails', async () => {
const showSnackBar = require('@utils/snack_bar').showSnackBar;
const errorMessage = 'Failed to delete scheduled post';
(deleteScheduledPost as jest.Mock).mockResolvedValueOnce({
error: new Error(errorMessage),
});

deleteScheduledPostConfirmation(baseProps);

// Get the confirm button callback
const confirmButton = (Alert.alert as jest.Mock).mock.calls[0][2][1];
await confirmButton.onPress();

expect(showSnackBar).toHaveBeenCalledWith({
barType: 'DELETE_SCHEDULED_POST_ERROR',
customMessage: errorMessage,
keepOpen: true,
type: 'error',
});
});
});

describe('getErrorStringFromCode', () => {
Expand Down

0 comments on commit d53d2d5

Please sign in to comment.