Unlicensed server should not show the tooltip on channel send button (#8806) (#8817)

* Unlicensed server should not show the tooltip on channel send button

* Added config and license check for fetching and displaying scheduled post

(cherry picked from commit 098c219932)

Co-authored-by: Rajat Dabade <rajatdabade1997@gmail.com>
This commit is contained in:
Mattermost Build 2025-04-29 16:49:20 +03:00 committed by GitHub
parent c771b301f6
commit efbaa26a11
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 82 additions and 16 deletions

View file

@ -5,7 +5,7 @@ import {forceLogoutIfNecessary} from '@actions/remote/session';
import {ActionType} from '@constants';
import DatabaseManager from '@database/manager';
import NetworkManager from '@managers/network_manager';
import {getConfigValue, getCurrentTeamId} from '@queries/servers/system';
import {getConfigValue, getCurrentTeamId, getLicense} from '@queries/servers/system';
import {logError} from '@utils/log';
import {createScheduledPost, deleteScheduledPost, fetchScheduledPosts, updateScheduledPost} from './scheduled_post';
@ -86,6 +86,7 @@ const mockWebSocketClient = {
};
jest.mock('@queries/servers/system', () => ({
getConfigValue: jest.fn(),
getLicense: jest.fn(),
getCurrentTeamId: jest.fn(),
getCurrentUserId: jest.fn(),
}));
@ -101,6 +102,7 @@ jest.mock('@utils/scheduled_post', () => {
});
const mockedGetConfigValue = jest.mocked(getConfigValue);
const mockedGetLicense = jest.mocked(getLicense);
beforeAll(() => {
// eslint-disable-next-line
@ -177,9 +179,18 @@ describe('fetchScheduledPosts', () => {
expect(mockClient.getScheduledPostsForTeam).not.toHaveBeenCalled();
});
it('handle scheduled post disabled is no license', async () => {
mockedGetConfigValue.mockResolvedValueOnce('true');
mockedGetLicense.mockResolvedValueOnce({IsLicensed: 'false'} as ClientLicense);
const result = await fetchScheduledPosts(serverUrl, 'bar');
expect(result.scheduledPosts).toEqual([]);
expect(mockClient.getScheduledPostsForTeam).not.toHaveBeenCalled();
});
it('handle scheduled post enabled', async () => {
jest.mocked(getCurrentTeamId).mockResolvedValue('bar');
mockedGetConfigValue.mockResolvedValueOnce('true');
mockedGetLicense.mockResolvedValueOnce({IsLicensed: 'true'} as ClientLicense);
const spyHandleScheduledPosts = jest.spyOn(operator, 'handleScheduledPosts');
const result = await fetchScheduledPosts(serverUrl, 'bar');
expect(result.scheduledPosts).toEqual(scheduledPostsResponse.bar);

View file

@ -5,7 +5,8 @@ import {ActionType} from '@constants';
import DatabaseManager from '@database/manager';
import NetworkManager from '@managers/network_manager';
import websocketManager from '@managers/websocket_manager';
import {getConfigValue, getCurrentUserId} from '@queries/servers/system';
import {getIsScheduledPostEnabled} from '@queries/servers/scheduled_post';
import {getCurrentUserId} from '@queries/servers/system';
import ScheduledPostModel from '@typings/database/models/servers/scheduled_post';
import {getFullErrorMessage} from '@utils/errors';
import {logError} from '@utils/log';
@ -71,7 +72,7 @@ export async function fetchScheduledPosts(serverUrl: string, teamId: string, inc
const client = NetworkManager.getClient(serverUrl);
const scheduledPostEnabled = (await getConfigValue(database, 'ScheduledPosts')) === 'true';
const scheduledPostEnabled = await getIsScheduledPostEnabled(database);
if (!scheduledPostEnabled) {
return {scheduledPosts: []};
}

View file

@ -260,6 +260,7 @@ function DraftInput({
disabled={sendActionDisabled}
sendMessage={handleSendMessage}
showScheduledPostOptions={handleShowScheduledPostOptions}
scheduledPostEnabled={scheduledPostsEnabled}
/>
</View>
</ScrollView>

View file

@ -3,14 +3,14 @@
import {withDatabase, withObservables} from '@nozbe/watermelondb/react';
import {observeConfigBooleanValue} from '@queries/servers/system';
import {observeScheduledPostEnabled} from '@queries/servers/scheduled_post';
import DraftInput from './draft_input';
import type {WithDatabaseArgs} from '@typings/database/database';
const enhanced = withObservables([], ({database}: WithDatabaseArgs) => {
const scheduledPostsEnabled = observeConfigBooleanValue(database, 'ScheduledPosts');
const scheduledPostsEnabled = observeScheduledPostEnabled(database);
return {
scheduledPostsEnabled,
};

View file

@ -9,11 +9,12 @@ import {SendButton} from '@components/post_draft/send_button/send_button';
import {fireEvent, renderWithIntl} from '@test/intl-test-helper';
describe('components/post_draft/send_button', () => {
const baseProps = {
const baseProps: Parameters<typeof SendButton>[0] = {
disabled: false,
sendMessage: jest.fn(),
testID: 'test_id',
showScheduledPostOptions: jest.fn(),
scheduledPostEnabled: true,
scheduledPostFeatureTooltipWatched: true,
};
@ -138,4 +139,22 @@ describe('components/post_draft/send_button', () => {
expect(queryByText(text)).toBeTruthy();
});
});
it('should not show the tooltip if the scheduled post feature is disabled', async () => {
const handle = InteractionManager.createInteractionHandle();
const props = {...baseProps, scheduledPostFeatureTooltipWatched: false, scheduledPostEnabled: false};
const {queryByText} = renderWithIntl(
<SendButton
{...props}
/>,
);
const text = 'Type a message and long press the send button to schedule it for a later time.';
InteractionManager.clearInteractionHandle(handle);
await new Promise((resolve) => setImmediate(resolve));
act(() => {
expect(queryByText(text)).toBeFalsy();
});
});
});

View file

@ -19,6 +19,7 @@ type Props = {
sendMessage: () => void;
showScheduledPostOptions: () => void;
scheduledPostFeatureTooltipWatched: boolean;
scheduledPostEnabled: boolean;
}
const getStyleSheet = makeStyleSheetFromTheme((theme) => {
@ -56,6 +57,7 @@ export function SendButton({
sendMessage,
showScheduledPostOptions,
scheduledPostFeatureTooltipWatched,
scheduledPostEnabled,
}: Props) {
const theme = useTheme();
const sendButtonTestID = `${testID}.send.button` + (disabled ? '.disabled' : '');
@ -64,7 +66,7 @@ export function SendButton({
const [scheduledPostTooltipVisible, setScheduledPostTooltipVisible] = useState(false);
useEffect(() => {
if (scheduledPostFeatureTooltipWatched) {
if (scheduledPostFeatureTooltipWatched || !scheduledPostEnabled) {
return;
}
@ -94,7 +96,7 @@ export function SendButton({
style={style.sendButtonContainer}
type={'opacity'}
disabled={disabled}
onLongPress={showScheduledPostOptions}
onLongPress={scheduledPostEnabled ? showScheduledPostOptions : undefined}
>
<Tooltip
isVisible={scheduledPostTooltipVisible}

View file

@ -2,10 +2,13 @@
// See LICENSE.txt for license information.
import {Database, Q} from '@nozbe/watermelondb';
import {of as of$} from 'rxjs';
import {combineLatest, of as of$} from 'rxjs';
import {switchMap, distinctUntilChanged} from 'rxjs/operators';
import {MM_TABLES} from '@constants/database';
import {getConfigValue, getLicense, observeConfigBooleanValue, observeLicense} from './system';
import type ScheduledPostModel from '@typings/database/models/servers/scheduled_post';
const {SERVER: {CHANNEL, SCHEDULED_POST}} = MM_TABLES;
@ -68,3 +71,24 @@ export const observeScheduledPostCountForThread = (database: Database, rootId: s
Q.where('root_id', rootId),
).observeCount();
};
export const observeScheduledPostEnabled = (database: Database) => {
const isScheduledPostConfigEnabled = observeConfigBooleanValue(database, 'ScheduledPosts');
const isLicensed = observeLicense(database);
const isScheduledPostEnabled = combineLatest([isScheduledPostConfigEnabled, isLicensed]).pipe(
switchMap(([isEnabled, license]) => {
if (license?.IsLicensed === 'true') {
return of$(isEnabled);
}
return of$(false);
}),
distinctUntilChanged(),
);
return isScheduledPostEnabled;
};
export const getIsScheduledPostEnabled = async (database: Database) => {
return (await getConfigValue(database, 'ScheduledPosts')) === 'true' && (await getLicense(database))?.IsLicensed === 'true';
};

View file

@ -2,11 +2,12 @@
// See LICENSE.txt for license information.
import {withDatabase, withObservables} from '@nozbe/watermelondb/react';
import {combineLatest, of as of$} from 'rxjs';
import {switchMap} from 'rxjs/operators';
import {observeDraftCount} from '@queries/servers/drafts';
import {observeScheduledPostCount} from '@queries/servers/scheduled_post';
import {observeConfigBooleanValue, observeCurrentTeamId} from '@queries/servers/system';
import {observeScheduledPostCount, observeScheduledPostEnabled} from '@queries/servers/scheduled_post';
import {observeCurrentTeamId} from '@queries/servers/system';
import GlobalDraftsAndScheduledPosts from './global_drafts';
@ -14,9 +15,16 @@ import type {WithDatabaseArgs} from '@typings/database/database';
const enhanced = withObservables([], ({database}: WithDatabaseArgs) => {
const currentTeamId = observeCurrentTeamId(database);
const scheduledPostsEnabled = observeConfigBooleanValue(database, 'ScheduledPosts');
const scheduledPostsEnabled = observeScheduledPostEnabled(database);
const draftsCount = currentTeamId.pipe(switchMap((teamId) => observeDraftCount(database, teamId))); // Observe draft count
const scheduledPostCount = currentTeamId.pipe(switchMap((teamId) => observeScheduledPostCount(database, teamId, true)));
const scheduledPostCount = combineLatest([currentTeamId, scheduledPostsEnabled]).pipe(
switchMap(([teamId, isEnabled]) => {
if (isEnabled) {
return observeScheduledPostCount(database, teamId, true);
}
return of$(0);
}),
);
return {
scheduledPostsEnabled,
draftsCount,

View file

@ -6,8 +6,8 @@ import {of} from 'rxjs';
import {switchMap} from 'rxjs/operators';
import {observeDraftCount} from '@queries/servers/drafts';
import {observeScheduledPostsForTeam} from '@queries/servers/scheduled_post';
import {observeConfigBooleanValue, observeCurrentTeamId} from '@queries/servers/system';
import {observeScheduledPostEnabled, observeScheduledPostsForTeam} from '@queries/servers/scheduled_post';
import {observeCurrentTeamId} from '@queries/servers/system';
import {observeTeamLastChannelId} from '@queries/servers/team';
import {hasScheduledPostError} from '@utils/scheduled_post';
@ -26,7 +26,7 @@ const enchanced = withObservables([], ({database}: WithDatabaseArgs) => {
const scheduledPostHasError = allScheduledPost.pipe(
switchMap((scheduledPosts) => of(hasScheduledPostError(scheduledPosts))),
);
const scheduledPostsEnabled = observeConfigBooleanValue(database, 'ScheduledPosts');
const scheduledPostsEnabled = observeScheduledPostEnabled(database);
return {
lastChannelId,