diff --git a/app/products/playbooks/database/queries/run.test.ts b/app/products/playbooks/database/queries/run.test.ts index ed61cede4..94c4f0599 100644 --- a/app/products/playbooks/database/queries/run.test.ts +++ b/app/products/playbooks/database/queries/run.test.ts @@ -3,7 +3,7 @@ /* eslint-disable max-lines */ -import {of as of$} from 'rxjs'; +import {of as of$, firstValueFrom} from 'rxjs'; import {MM_TABLES} from '@constants/database'; import DatabaseManager from '@database/manager'; @@ -18,6 +18,7 @@ import { getLastPlaybookRunsFetchAt, queryParticipantsFromAPIRun, observeParticipantsIdsFromPlaybookModel, + observeHasRunningPlaybookRunsInTeam, } from './run'; import type ServerDataOperator from '@database/operator/server_data_operator'; @@ -763,4 +764,111 @@ describe('Playbook Run Queries', () => { expect(runIds).not.toContain(mockRuns[1].id); }); }); + + describe('observeHasRunningPlaybookRunsInTeam', () => { + it('should return true when there are running playbook runs in the team', async () => { + const teamId = 'team-id-1'; + const mockRuns = TestHelper.createPlaybookRuns(1, 0, 0).map((run) => ({ + ...run, + team_id: teamId, + end_at: 0, + })); + + await operator.handlePlaybookRun({ + runs: mockRuns, + prepareRecordsOnly: false, + removeAssociatedRecords: false, + }); + + const result = await firstValueFrom(observeHasRunningPlaybookRunsInTeam(operator.database, teamId)); + + expect(result).toBe(true); + }); + + it('should return false when all runs in the team are finished', async () => { + const teamId = 'team-id-1'; + const mockRuns = TestHelper.createPlaybookRuns(2, 0, 0).map((run) => ({ + ...run, + team_id: teamId, + end_at: 1620000000000, + })); + + await operator.handlePlaybookRun({ + runs: mockRuns, + prepareRecordsOnly: false, + removeAssociatedRecords: false, + }); + + const result = await firstValueFrom(observeHasRunningPlaybookRunsInTeam(operator.database, teamId)); + + expect(result).toBe(false); + }); + + it('should return false when no runs exist in the team', async () => { + const teamId = 'team-id-1'; + + const result = await firstValueFrom(observeHasRunningPlaybookRunsInTeam(operator.database, teamId)); + + expect(result).toBe(false); + }); + + it('should only count runs from the specified team', async () => { + const teamId = 'team-id-1'; + const otherTeamId = 'team-id-2'; + + const mockRuns = [ + { + ...TestHelper.createPlaybookRuns(1, 0, 0)[0], + id: 'run-1', + team_id: teamId, + end_at: 1620000000000, // Finished in target team + }, + { + ...TestHelper.createPlaybookRuns(1, 0, 0)[0], + id: 'run-2', + team_id: otherTeamId, + end_at: 0, // Running in other team + }, + ]; + + await operator.handlePlaybookRun({ + runs: mockRuns, + prepareRecordsOnly: false, + removeAssociatedRecords: false, + }); + + const result = await firstValueFrom(observeHasRunningPlaybookRunsInTeam(operator.database, teamId)); + + expect(result).toBe(false); + }); + + it('should return true when at least one run is in progress among mixed runs', async () => { + const teamId = 'team-id-1'; + + const mockRuns = [ + { + ...TestHelper.createPlaybookRuns(1, 0, 0)[0], + id: 'run-1', + team_id: teamId, + end_at: 1620000000000, // Finished + }, + { + ...TestHelper.createPlaybookRuns(1, 0, 0)[0], + id: 'run-2', + team_id: teamId, + end_at: 0, // Running + }, + ]; + + await operator.handlePlaybookRun({ + runs: mockRuns, + prepareRecordsOnly: false, + removeAssociatedRecords: false, + }); + + const result = await firstValueFrom(observeHasRunningPlaybookRunsInTeam(operator.database, teamId)); + + expect(result).toBe(true); + }); + }); }); diff --git a/app/products/playbooks/database/queries/run.ts b/app/products/playbooks/database/queries/run.ts index 12187f4da..b4b381152 100644 --- a/app/products/playbooks/database/queries/run.ts +++ b/app/products/playbooks/database/queries/run.ts @@ -97,3 +97,14 @@ export const queryPlaybookRunsByParticipantAndTeam = (database: Database, partic Q.sortBy('create_at', 'desc'), ); }; + +export const observeHasRunningPlaybookRunsInTeam = (database: Database, teamId: string) => { + return database.get(PLAYBOOK_RUN).query( + Q.and( + Q.where('team_id', teamId), + Q.where('end_at', Q.eq(0)), + ), + ).observeCount().pipe( + switchMap((count) => of$(count > 0)), + ); +}; diff --git a/app/screens/home/channel_list/categories_list/categories_list.tsx b/app/screens/home/channel_list/categories_list/categories_list.tsx index 26838ca35..31c9c1189 100644 --- a/app/screens/home/channel_list/categories_list/categories_list.tsx +++ b/app/screens/home/channel_list/categories_list/categories_list.tsx @@ -38,7 +38,7 @@ type ChannelListProps = { scheduledPostHasError: boolean; lastChannelId?: string; scheduledPostsEnabled?: boolean; - playbooksEnabled?: boolean; + showPlaybooksButton?: boolean; }; const getTabletWidth = (moreThanOneTeam: boolean) => { @@ -57,7 +57,7 @@ const CategoriesList = ({ scheduledPostHasError, lastChannelId, scheduledPostsEnabled, - playbooksEnabled, + showPlaybooksButton, }: ChannelListProps) => { const theme = useTheme(); const styles = getStyleSheet(theme); @@ -128,14 +128,14 @@ const CategoriesList = ({ }, [activeScreen, draftsCount, isTablet, scheduledPostCount, scheduledPostHasError, scheduledPostsEnabled]); const playbooksButtonComponent = useMemo(() => { - if (!playbooksEnabled) { + if (!showPlaybooksButton) { return null; } return ( ); - }, [playbooksEnabled]); + }, [showPlaybooksButton]); const content = useMemo(() => { if (!hasChannels) { diff --git a/app/screens/home/channel_list/categories_list/index.test.tsx b/app/screens/home/channel_list/categories_list/index.test.tsx index 9ae0cc385..279791213 100644 --- a/app/screens/home/channel_list/categories_list/index.test.tsx +++ b/app/screens/home/channel_list/categories_list/index.test.tsx @@ -171,7 +171,7 @@ describe('components/categories_list', () => { expect(wrapper.queryByText('Drafts')).not.toBeTruthy(); }); - it('should not render channel list with Playbooks menu if playbooks feature is disabled', () => { + it('should not render Playbooks menu when showPlaybooksButton is false', () => { const wrapper = renderWithEverything( { draftsCount={0} scheduledPostCount={0} scheduledPostHasError={false} - playbooksEnabled={false} + showPlaybooksButton={false} />, {database}, ); - expect(wrapper.queryByText('Playbook checklists')).not.toBeTruthy(); + expect(wrapper.queryByText('Playbook checklists')).toBeNull(); }); - it('should render channel list with Playbooks menu if playbooks feature is enabled', () => { + it('should render Playbooks menu when showPlaybooksButton is true', () => { const wrapper = renderWithEverything( { draftsCount={0} scheduledPostCount={0} scheduledPostHasError={false} - playbooksEnabled={true} + showPlaybooksButton={true} />, {database}, ); diff --git a/app/screens/home/channel_list/categories_list/index.tsx b/app/screens/home/channel_list/categories_list/index.tsx index 3037c8a3e..cf893a709 100644 --- a/app/screens/home/channel_list/categories_list/index.tsx +++ b/app/screens/home/channel_list/categories_list/index.tsx @@ -2,9 +2,10 @@ // See LICENSE.txt for license information. import {withDatabase, withObservables} from '@nozbe/watermelondb/react'; -import {of} from 'rxjs'; +import {combineLatest, of} from 'rxjs'; import {switchMap} from 'rxjs/operators'; +import {observeHasRunningPlaybookRunsInTeam} from '@playbooks/database/queries/run'; import {observeIsPlaybooksEnabled} from '@playbooks/database/queries/version'; import {observeDraftCount} from '@queries/servers/drafts'; import {observeScheduledPostEnabled, observeScheduledPostsForTeam} from '@queries/servers/scheduled_post'; @@ -28,7 +29,13 @@ const enchanced = withObservables([], ({database}: WithDatabaseArgs) => { switchMap((scheduledPosts) => of(hasScheduledPostError(scheduledPosts))), ); const scheduledPostsEnabled = observeScheduledPostEnabled(database); - const playbooksEnabled = observeIsPlaybooksEnabled(database); + const showPlaybooksButton = currentTeamId.pipe( + switchMap((teamId) => combineLatest([ + observeIsPlaybooksEnabled(database), + observeHasRunningPlaybookRunsInTeam(database, teamId), + ])), + switchMap(([enabled, hasRunningRuns]) => of(enabled && hasRunningRuns)), + ); return { lastChannelId, @@ -36,7 +43,7 @@ const enchanced = withObservables([], ({database}: WithDatabaseArgs) => { scheduledPostCount, scheduledPostHasError, scheduledPostsEnabled, - playbooksEnabled, + showPlaybooksButton, }; });