Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create scheduled post api integration #8519

Merged
Merged
Show file tree
Hide file tree
Changes from 72 commits
Commits
Show all changes
87 commits
Select commit Hold shift + click to select a range
b7d0ba6
WIP
harshilsharma63 Jan 16, 2025
cb9a62b
Separate component for core and custom options
harshilsharma63 Jan 17, 2025
b2b144d
WIP
harshilsharma63 Jan 17, 2025
7a13704
WIP
harshilsharma63 Jan 17, 2025
52bfcdd
WIP
harshilsharma63 Jan 17, 2025
08c9a53
Merge branch 'feature_schedule_posts' into show_scheduled_post_options
harshilsharma63 Jan 20, 2025
5ea68e1
unified options into single component
harshilsharma63 Jan 20, 2025
2a2d101
Scheduled post menu ready
harshilsharma63 Jan 20, 2025
37aa32b
Merge branch 'feature_schedule_posts' into show_scheduled_post_options
harshilsharma63 Jan 21, 2025
d62bb3c
Displayed date time picker by default
harshilsharma63 Jan 21, 2025
4be9065
WIP
harshilsharma63 Jan 22, 2025
96dcab2
Lint fix
harshilsharma63 Jan 22, 2025
7dc66b5
i18n fix
harshilsharma63 Jan 22, 2025
27344f1
removed unused screen
harshilsharma63 Jan 22, 2025
34f7ced
API call sent successfully
harshilsharma63 Jan 22, 2025
e5527d0
Merge branch 'feature_schedule_posts' into show_scheduled_post_options
harshilsharma63 Jan 23, 2025
4e893a1
Added user time format preference
harshilsharma63 Jan 23, 2025
d34305d
review fixex and improvements
harshilsharma63 Jan 23, 2025
d6ab9d1
Merge branch 'show_scheduled_post_options' into create_scheduled_post…
harshilsharma63 Jan 23, 2025
2edf74a
Handled loading sttae
harshilsharma63 Jan 23, 2025
976af46
Handled loading sttae
harshilsharma63 Jan 23, 2025
e832f05
Handled mentions and files and priority
harshilsharma63 Jan 24, 2025
82c8fb0
Cleanup
harshilsharma63 Jan 24, 2025
f08a7e6
lint fix
harshilsharma63 Jan 24, 2025
1498cb9
i18n fix
harshilsharma63 Jan 24, 2025
8a63e96
WIP
harshilsharma63 Jan 24, 2025
d443026
Displayed error snackbar
harshilsharma63 Jan 27, 2025
5ade7cd
Fixed time used for debugging
harshilsharma63 Jan 27, 2025
bfaf507
Lint fix
harshilsharma63 Jan 27, 2025
06dbcdf
Merge branch 'feature_schedule_posts' into show_scheduled_post_options
harshilsharma63 Jan 27, 2025
bdc6113
Merge branch 'show_scheduled_post_options' into create_scheduled_post…
harshilsharma63 Jan 27, 2025
cd62280
Merge branch 'feature_schedule_posts' into show_scheduled_post_options
harshilsharma63 Jan 28, 2025
7699011
Merge branch 'show_scheduled_post_options' into create_scheduled_post…
harshilsharma63 Jan 28, 2025
69efb06
review fixex and improvements
harshilsharma63 Jan 28, 2025
5141c7d
Merge branch 'feature_schedule_posts' into show_scheduled_post_options
harshilsharma63 Jan 29, 2025
c06a248
Renamed a file
harshilsharma63 Jan 29, 2025
7ad7a15
Merge branch 'show_scheduled_post_options' into create_scheduled_post…
harshilsharma63 Jan 29, 2025
637d5d0
Adding tests
harshilsharma63 Jan 29, 2025
2d6eb3b
Adding tests
harshilsharma63 Jan 29, 2025
4c1e54f
lint fix
harshilsharma63 Jan 29, 2025
90b28aa
Merge branch 'feature_schedule_posts' into create_scheduled_post_api_…
harshilsharma63 Jan 29, 2025
40c3f8d
cleanup
harshilsharma63 Jan 29, 2025
b57c3c3
Merge branch 'feature_schedule_posts' into create_scheduled_post_api_…
harshilsharma63 Jan 30, 2025
dd5bb8c
Added draft_input tests
harshilsharma63 Jan 30, 2025
f3a09f0
test: Add comprehensive tests for SendAction component
harshilsharma63 Jan 30, 2025
d322d8d
Added send_action tests
harshilsharma63 Jan 30, 2025
e8d9f46
lint fix
harshilsharma63 Jan 30, 2025
0c5cd53
test: Add comprehensive test coverage for useHandleSendMessage hook
harshilsharma63 Jan 30, 2025
a69e820
test: Add test for scheduled post creation in send message hook
harshilsharma63 Jan 30, 2025
08d125a
fix: Mock createScheduledPost function in test to resolve matcher error
harshilsharma63 Jan 30, 2025
16158b4
WIP tests
harshilsharma63 Jan 31, 2025
48db248
test: Add tests for SnackBar component
harshilsharma63 Jan 31, 2025
7350939
Added snack bar tests
harshilsharma63 Jan 31, 2025
fe617c9
Cleanup
harshilsharma63 Jan 31, 2025
8ebfd42
test: Add empty test file for scheduled post picker
harshilsharma63 Jan 31, 2025
2f91aa9
test: Add tests for ScheduledPostOptions component
harshilsharma63 Jan 31, 2025
20a1dd8
test: Improve ScheduledPostOptions test coverage and error handling
harshilsharma63 Jan 31, 2025
c3bb8a4
test: Update ScheduledPostOptions test suite with new database and ti…
harshilsharma63 Jan 31, 2025
2edc2b8
test: Fix timeout issues in ScheduledPostOptions test suite
harshilsharma63 Jan 31, 2025
d280d27
Added scheuidled post picker tests
harshilsharma63 Jan 31, 2025
ed7c9ad
test: Add empty test file for scheduled post footer
harshilsharma63 Jan 31, 2025
37a0acc
test: Add comprehensive tests for ScheduledPostFooter component
harshilsharma63 Jan 31, 2025
7ba4d9c
feat: Add animatedFooterPosition prop to ScheduledPostFooter test
harshilsharma63 Jan 31, 2025
2e4e447
test: Add tablet rendering test for ScheduledPostFooter
harshilsharma63 Jan 31, 2025
8595da3
test: Update ScheduledPostFooter test mocks and remove unused imports
harshilsharma63 Jan 31, 2025
688ef6d
test: Add mocks for BottomSheet context in scheduled post footer tests
harshilsharma63 Jan 31, 2025
d64f8e0
Added scheudled post footer test
harshilsharma63 Jan 31, 2025
eac1e87
test: Add comprehensive tests for handle_send_message hook
harshilsharma63 Jan 31, 2025
35e7460
Enhanced handle send message tests
harshilsharma63 Jan 31, 2025
e1fbd58
cleanup
harshilsharma63 Jan 31, 2025
bbf2cba
Added missing snapshots
harshilsharma63 Jan 31, 2025
be5ba6b
Merge branch 'feature_schedule_posts' into create_scheduled_post_api_…
harshilsharma63 Jan 31, 2025
a907328
test: Add error logging and force logout validation for scheduled pos…
harshilsharma63 Feb 3, 2025
b34fef9
Updated test
harshilsharma63 Feb 3, 2025
67c03c1
used toBeVisible instead of toBeTruthy
harshilsharma63 Feb 3, 2025
2c89ccb
test: Update test assertions to use toHaveBeenCalledWith
harshilsharma63 Feb 3, 2025
8086888
test: Add assertions to prevent sendMessage when send button is disabled
harshilsharma63 Feb 3, 2025
db90702
test: Replace snapshot tests with explicit UI element checks
harshilsharma63 Feb 3, 2025
df72fba
test improvements
harshilsharma63 Feb 3, 2025
d4cf535
Deleted unused snapshots
harshilsharma63 Feb 3, 2025
b940f71
Updated snaopshot
harshilsharma63 Feb 3, 2025
5a2cf9c
feat: Add close button ID for scheduled post options bottom sheet
harshilsharma63 Feb 3, 2025
9da20ba
test: Add mock scheduling flow to scheduled post options test
harshilsharma63 Feb 3, 2025
5de1382
Fixed incorrect test
harshilsharma63 Feb 3, 2025
fccde48
scheudled post test enhancement
harshilsharma63 Feb 3, 2025
a9ffd89
minimised a test
harshilsharma63 Feb 3, 2025
cb2e36e
Scheduled post indicator (#8522)
harshilsharma63 Feb 4, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 72 additions & 0 deletions app/actions/remote/scheduled_post.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.

import DatabaseManager from '@database/manager';
import NetworkManager from '@managers/network_manager';

import {createScheduledPost} from './scheduled_post';

import type ServerDataOperator from '@database/operator/server_data_operator';

const serverUrl = 'baseHandler.test.com';
let operator: ServerDataOperator;

const user1 = {id: 'userid1', username: 'user1', email: '[email protected]', roles: ''} as UserProfile;
const scheduledPost = {
id: 'scheduledPostId1',
root_id: '',
update_at: 0,
channel_id: 'channelid1',
message: 'Test message',
scheduled_at: Date.now() + 10000,
} as ScheduledPost;

const throwFunc = () => {
throw Error('error');
};

const mockClient = {
createScheduledPost: jest.fn(() => ({...scheduledPost})),
};

beforeAll(() => {
// eslint-disable-next-line
// @ts-ignore
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you need both?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, removed and used ts-expect-error, otherwise we'd need both.

NetworkManager.getClient = () => mockClient;
});

beforeEach(async () => {
await DatabaseManager.init([serverUrl]);
operator = DatabaseManager.serverDatabases[serverUrl]!.operator;
});

afterEach(async () => {
await DatabaseManager.destroyServerDatabase(serverUrl);
});

describe('scheduled_post', () => {
it('createScheduledPost - handle not found database', async () => {
const result = await createScheduledPost('foo', scheduledPost);
expect(result).toBeDefined();
expect(result.error).toBeDefined();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Kinda redundant. If you have result.error, you know result is defined.

But it would be more beneficial to know the error is what you expect. Like 'database not found' or something.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

});

it('createScheduledPost - base case', async () => {
await operator.handleUsers({users: [user1], prepareRecordsOnly: false});
const result = await createScheduledPost(serverUrl, scheduledPost);
expect(result).toBeDefined();
expect(result.error).toBeUndefined();
expect(result.data).toBe(true);
expect(result.response).toBeDefined();
if (result.response) {
expect(result.response.id).toBe(scheduledPost.id);
}
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above, I think you can skip result toBeDefined().

The check for result.response is unnecessary if you have already expect result.response toBeDefined() right?

I barely see any need for if-else statements in test. You're not creating conditions in your test, you're trying to paint a story of what you're expecting.

In this instance:

expect(result.response.id).toBe(scheduledPost.id);

negates the need to check if it's defined, or that result.response is true/exists.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.


it('createScheduledPost - request error', async () => {
mockClient.createScheduledPost.mockImplementationOnce(jest.fn(throwFunc));
const result = await createScheduledPost(serverUrl, scheduledPost);
expect(result).toBeDefined();
expect(result.error).toBeDefined();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nitpick: We could check that on error, we are also logging the error and calling logout if necessary.

We would just have to mock the logDebug and the forceLogoutIfNecessary and making sure they are called.

You could also make sure the error logged / returned is the one returned by the client function.

0/5 if we have to be so meticulous though

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like it meticulous. Say somebody remove the forceLogoutIfNecessary by accident, that meticulousness will flag and bring a discussion if that removal is necessary.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

});
});
24 changes: 13 additions & 11 deletions app/actions/remote/scheduled_post.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,28 @@
// See LICENSE.txt for license information.

import {forceLogoutIfNecessary} from '@actions/remote/session';
import DatabaseManager from '@database/manager';
import NetworkManager from '@managers/network_manager';
import websocketManager from '@managers/websocket_manager';
import {getFullErrorMessage} from '@utils/errors';
import {logError} from '@utils/log';

export async function createScheduledPost(serverUrl: string, schedulePost: ScheduledPost, connectionId?: string, fetchOnly = false) {
import type {CreateResponse} from '@hooks/handle_send_message';

export async function createScheduledPost(serverUrl: string, schedulePost: ScheduledPost, connectionId?: string): Promise<CreateResponse> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Not a request for changes, just a knowledge share)
In general for remote actions we add another optional parameter fetchOnly, default to false. This is so we can decide not to update the local state when we get certain information. This may be done for several reasons, like fetching data that is not going to be kept up to date (nor by websocket nor syncing logic), so we don't want it in the database. This could be, for example, browse channels results.

In this particular case, I don't foresee any case where we want to create a schedule post and not update the local database, so it should be just fine not having the parameter.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had fetchOnly but since this doesn't make aAPI calls, the linter complained about an ununsed param and I removed it. I will add it when needed.

const operator = DatabaseManager.serverDatabases[serverUrl]?.operator;
if (!operator) {
return {error: `${serverUrl} database not found`};
}

try {
const client = NetworkManager.getClient(serverUrl);
const ws = websocketManager.getClient(serverUrl);

const created = await client.createScheduledPost(schedulePost, ws?.getConnectionId());
const response = await client.createScheduledPost(schedulePost, connectionId);

if (!fetchOnly) {
// TODO - to be implemented later once DB tables are ready
// await operator.handleScheduledPost({scheduledPosts: [created], prepareRecordsOnly: false});
}
return {scheduledPost: created};
// TODO - record scheduled post in database here
return {data: true, response};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still think that data should be the response directly, not a boolean. See for example fetchThread.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would tend to agree with you @larkox, but apparently this is an else for createPost(), which also returns {data: true/false}.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It has to be data: boolean and response separately as doSubmitMessage call either createPost or createScheduledPost and both need to return the same structure. Create Post returns data: boolean, and so for that reason, so does createScheduledPost have to and updating createPost is beyond the scope of this feature.

} catch (error) {
logError('error on createScheduledPost', getFullErrorMessage(error));
forceLogoutIfNecessary(serverUrl, error);
return {error};
return {error: getFullErrorMessage(error)};
}
}
166 changes: 166 additions & 0 deletions app/components/post_draft/draft_input/draft_input.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.

import {fireEvent} from '@testing-library/react-native';
import React from 'react';

import {Screens} from '@constants';
import {PostPriorityType} from '@constants/post';
import NetworkManager from '@managers/network_manager';
import {openAsBottomSheet} from '@screens/navigation';
import {renderWithEverything} from '@test/intl-test-helper';
import TestHelper from '@test/test_helper';
import {persistentNotificationsConfirmation} from '@utils/post';

import DraftInput from './draft_input';

import type ServerDataOperator from '@database/operator/server_data_operator';
import type {Database} from '@nozbe/watermelondb';

const SERVER_URL = 'https://appv1.mattermost.com';

// this is needed to when using the useServerUrl hook
jest.mock('@context/server', () => ({
useServerUrl: jest.fn(() => SERVER_URL),
}));

jest.mock('@screens/navigation', () => ({
openAsBottomSheet: jest.fn(),
}));

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

describe('DraftInput', () => {
const baseProps = {
testID: 'draft_input',
channelId: 'channelId',
channelType: 'O' as ChannelType,
currentUserId: 'currentUserId',
postPriority: {priority: ''} as PostPriority,
updatePostPriority: jest.fn(),
persistentNotificationInterval: 0,
persistentNotificationMaxRecipients: 0,
updateCursorPosition: jest.fn(),
cursorPosition: 0,
sendMessage: jest.fn(),
canSend: true,
maxMessageLength: 4000,
files: [],
value: '',
uploadFileError: null,
updateValue: jest.fn(),
addFiles: jest.fn(),
updatePostInputTop: jest.fn(),
setIsFocused: jest.fn(),
scheduledPostsEnabled: true,
};

beforeEach(() => {
jest.clearAllMocks();
});

let database: Database;
let operator: ServerDataOperator;

beforeEach(async () => {
const server = await TestHelper.setupServerDatabase(SERVER_URL);
database = server.database;
operator = server.operator;
});

afterEach(async () => {
await TestHelper.tearDown();
NetworkManager.invalidateClient(SERVER_URL);
});

describe('Rendering', () => {
it('renders base components', async () => {
const {getByTestId} = renderWithEverything(<DraftInput {...baseProps}/>, {database});
expect(getByTestId('draft_input')).toBeTruthy();
expect(getByTestId('draft_input.post.input')).toBeTruthy();
expect(getByTestId('draft_input.quick_actions')).toBeTruthy();
expect(getByTestId('draft_input.send_action.send.button')).toBeTruthy();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any strong opinion on why to use toBeTruthy and not toBeVisible? In theory toBeVisible will check a few more interesting things, like that it has a size, but 0/5.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. Updated elsewhere as well.

});

it('shows upload error when present', () => {
const props = {...baseProps, uploadFileError: 'Error message'};
const {getByText} = renderWithEverything(<DraftInput {...props}/>, {database});
expect(getByText('Error message')).toBeTruthy();
});
});

describe('Message Actions', () => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are a couple of details you are not testing. Mainly that you are passing the right action to the popups (i.e. openAsBottomSheet and persistentNotificationsConfirmation).

What could be done to test that is the following:

  • Mock the implementation of openAsBottomSheet (and similar for persistentNotificationsConfirmation) like this:
jest.mocked(openAsBottomSheet).mockImplementation(({props: {onSchedule}}) => onSchedule(SCHEDULING_INFO))
  • Verify send message is called
  • Verify that if you had post priority, you still get the warning

This way you verify that if the bottom sheet (or the verification for priority messages) does what you expect them to do, your code does things right (in other words... you are passing the right functions into the right places).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

it('sends message on press', () => {
const {getByTestId} = renderWithEverything(<DraftInput {...baseProps}/>, {database});
fireEvent.press(getByTestId('draft_input.send_action.send.button'));
expect(baseProps.sendMessage).toHaveBeenCalled();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should you verify it has been called with no arguments?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

^^

Would it be beneficial to use toHaveBeenCalledWith() instead, and confirm sendMessage() is passing the right arguments?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

});

it('opens scheduled post options on long press', async () => {
// make this a re-usable function
await operator.handleConfigs({
configs: [
{id: 'ScheduledPosts', value: 'true'},
],
configsToDelete: [],
prepareRecordsOnly: false,
});

const {getByTestId} = renderWithEverything(<DraftInput {...baseProps}/>, {database});
fireEvent(getByTestId('draft_input.send_action.send.button'), 'longPress');
expect(openAsBottomSheet).toHaveBeenCalledWith(expect.objectContaining({
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just as sanity check, we could add here also a check to make sure the other function (sendMessage if I am not mistaken) is not called.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

screen: Screens.SCHEDULED_POST_OPTIONS,
}));
});

it('handles persistent notifications', async () => {
jest.mocked(persistentNotificationsConfirmation).mockResolvedValueOnce();
const props = {
...baseProps,
postPriority: {
persistent_notifications: true,
priority: PostPriorityType.URGENT,
} as PostPriority,
value: '@user1 @user2 message',
};

const {getByTestId} = renderWithEverything(<DraftInput {...props}/>, {database});
fireEvent.press(getByTestId('draft_input.send_action.send.button'));
expect(persistentNotificationsConfirmation).toHaveBeenCalled();
});
});

describe('Input Handling', () => {
it('updates text value', () => {
const {getByTestId} = renderWithEverything(<DraftInput {...baseProps}/>, {database});
fireEvent.changeText(getByTestId('draft_input.post.input'), 'new message');
expect(baseProps.updateValue).toHaveBeenCalledWith('new message');
});

it('handles cursor position', () => {
const {getByTestId} = renderWithEverything(<DraftInput {...baseProps}/>, {database});
fireEvent(getByTestId('draft_input.post.input'), 'selectionChange', {
nativeEvent: {selection: {start: 5, end: 5}},
});
expect(baseProps.updateCursorPosition).toHaveBeenCalledWith(5);
});

it('updates focus state', () => {
const {getByTestId} = renderWithEverything(<DraftInput {...baseProps}/>, {database});
fireEvent(getByTestId('draft_input.post.input'), 'focus');
expect(baseProps.setIsFocused).toHaveBeenCalledWith(true);
});
});

describe('Validation', () => {
it('disables send when canSend is false', () => {
const props = {...baseProps, canSend: false};
const {getByTestId} = renderWithEverything(<DraftInput {...props}/>, {database});
const sendButton = getByTestId('draft_input.send_action.send.button.disabled');
expect(sendButton).toBeTruthy();
expect(sendButton).toBeDisabled();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, just as sanity check, we could double check that both press and long press actions do not call the functions, since the button is disabled.

But that may be already covered by the button being marked as disabled...

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

});
});
});
20 changes: 12 additions & 8 deletions app/components/post_draft/draft_input/draft_input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import Header from './header';
import type {PasteInputRef} from '@mattermost/react-native-paste-input';
import type {WithDatabaseArgs} from '@typings/database/database';

type Props = {
export type Props = {
testID?: string;
channelId: string;
channelType?: ChannelType;
Expand All @@ -48,7 +48,7 @@ type Props = {
cursorPosition: number;

// Send Handler
sendMessage: () => void;
sendMessage: (schedulingInfo?: SchedulingInfo) => Promise<void | {data?: boolean; error?: unknown}>;
canSend: boolean;
maxMessageLength: number;

Expand Down Expand Up @@ -164,13 +164,16 @@ function DraftInput({
postPriority,
});

const handleSendMessage = useCallback(async () => {
const handleSendMessage = useCallback(async (schedulingInfoParam?: SchedulingInfo) => {
const schedulingInfo = (schedulingInfoParam && 'scheduled_at' in schedulingInfoParam) ? schedulingInfoParam : undefined;

if (persistentNotificationsEnabled) {
persistentNotificationsConfirmation(serverUrl, value, mentionsList, intl, sendMessage, persistentNotificationMaxRecipients, persistentNotificationInterval, currentUserId, channelName, channelType);
} else {
sendMessage();
const sendMessageWithScheduledPost = () => sendMessage(schedulingInfo);
await persistentNotificationsConfirmation(serverUrl, value, mentionsList, intl, sendMessageWithScheduledPost, persistentNotificationMaxRecipients, persistentNotificationInterval, currentUserId, channelName, channelType);
return Promise.resolve();
}
}, [serverUrl, mentionsList, persistentNotificationsEnabled, persistentNotificationMaxRecipients, sendMessage, value, channelType]);
return sendMessage(schedulingInfo);
}, [persistentNotificationsEnabled, serverUrl, value, mentionsList, intl, sendMessage, persistentNotificationMaxRecipients, persistentNotificationInterval, currentUserId, channelName, channelType]);

const handleShowScheduledPostOptions = useCallback(() => {
if (!scheduledPostsEnabled) {
Expand All @@ -187,9 +190,10 @@ function DraftInput({
title,
props: {
closeButtonId: SCHEDULED_POST_PICKER_BUTTON,
onSchedule: handleSendMessage,
},
});
}, [intl, isTablet, scheduledPostsEnabled, theme]);
}, [handleSendMessage, intl, isTablet, scheduledPostsEnabled, theme]);

const sendActionDisabled = !canSend || noMentionsError;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`components/post_draft/send_action should match snapshot 1`] = `
<View
accessibilityState={
{
"busy": undefined,
"checked": undefined,
"disabled": false,
"expanded": undefined,
"selected": undefined,
}
}
accessibilityValue={
{
"max": undefined,
"min": undefined,
"now": undefined,
"text": undefined,
}
}
accessible={true}
collapsable={false}
focusable={true}
onClick={[Function]}
onResponderGrant={[Function]}
onResponderMove={[Function]}
onResponderRelease={[Function]}
onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]}
style={
{
"justifyContent": "flex-end",
"opacity": 1,
"paddingRight": 8,
}
}
testID="test_id.send.button"
>
<View
style={
{
"alignItems": "center",
"backgroundColor": "#1c58d9",
"borderRadius": 4,
"height": 32,
"justifyContent": "center",
"width": 80,
}
}
>
<Icon
color="#ffffff"
name="send"
size={24}
/>
</View>
</View>
`;
Loading
Loading