From efbaa26a117ed431a258f2338f00113caa2f626c Mon Sep 17 00:00:00 2001 From: Mattermost Build Date: Tue, 29 Apr 2025 16:49:20 +0300 Subject: [PATCH] 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 098c21993233da3a89e1e8e05c3772c1eeb0d2fd) Co-authored-by: Rajat Dabade --- app/actions/remote/scheduled_post.test.ts | 13 +++++++++- app/actions/remote/scheduled_post.ts | 5 ++-- .../post_draft/draft_input/draft_input.tsx | 1 + .../post_draft/draft_input/index.ts | 4 +-- .../send_button/send_button.test.tsx | 21 ++++++++++++++- .../post_draft/send_button/send_button.tsx | 6 +++-- app/queries/servers/scheduled_post.ts | 26 ++++++++++++++++++- app/screens/global_drafts/index.ts | 16 +++++++++--- .../channel_list/categories_list/index.tsx | 6 ++--- 9 files changed, 82 insertions(+), 16 deletions(-) diff --git a/app/actions/remote/scheduled_post.test.ts b/app/actions/remote/scheduled_post.test.ts index ba95780ee..60abb2ad5 100644 --- a/app/actions/remote/scheduled_post.test.ts +++ b/app/actions/remote/scheduled_post.test.ts @@ -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); diff --git a/app/actions/remote/scheduled_post.ts b/app/actions/remote/scheduled_post.ts index ea2b16ee2..29aeaf17c 100644 --- a/app/actions/remote/scheduled_post.ts +++ b/app/actions/remote/scheduled_post.ts @@ -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: []}; } diff --git a/app/components/post_draft/draft_input/draft_input.tsx b/app/components/post_draft/draft_input/draft_input.tsx index d12a4451d..9235edfef 100644 --- a/app/components/post_draft/draft_input/draft_input.tsx +++ b/app/components/post_draft/draft_input/draft_input.tsx @@ -260,6 +260,7 @@ function DraftInput({ disabled={sendActionDisabled} sendMessage={handleSendMessage} showScheduledPostOptions={handleShowScheduledPostOptions} + scheduledPostEnabled={scheduledPostsEnabled} /> diff --git a/app/components/post_draft/draft_input/index.ts b/app/components/post_draft/draft_input/index.ts index 4ebba7a39..7e6ea359c 100644 --- a/app/components/post_draft/draft_input/index.ts +++ b/app/components/post_draft/draft_input/index.ts @@ -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, }; diff --git a/app/components/post_draft/send_button/send_button.test.tsx b/app/components/post_draft/send_button/send_button.test.tsx index 287574bc1..b4ca88016 100644 --- a/app/components/post_draft/send_button/send_button.test.tsx +++ b/app/components/post_draft/send_button/send_button.test.tsx @@ -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[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( + , + ); + 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(); + }); + }); }); diff --git a/app/components/post_draft/send_button/send_button.tsx b/app/components/post_draft/send_button/send_button.tsx index af9d0a0ef..1c53318ee 100644 --- a/app/components/post_draft/send_button/send_button.tsx +++ b/app/components/post_draft/send_button/send_button.tsx @@ -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} > { + 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'; +}; diff --git a/app/screens/global_drafts/index.ts b/app/screens/global_drafts/index.ts index 949478034..e3ca6092d 100644 --- a/app/screens/global_drafts/index.ts +++ b/app/screens/global_drafts/index.ts @@ -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, diff --git a/app/screens/home/channel_list/categories_list/index.tsx b/app/screens/home/channel_list/categories_list/index.tsx index ac81e1550..c09089c49 100644 --- a/app/screens/home/channel_list/categories_list/index.tsx +++ b/app/screens/home/channel_list/categories_list/index.tsx @@ -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,