Skip to content

Commit

Permalink
Merge branch 'develop' into fix/e2ee_copy_link_Starred_and_Pinned
Browse files Browse the repository at this point in the history
  • Loading branch information
hugocostadev authored Jul 23, 2024
2 parents 4b145db + 7e8e003 commit 13793dc
Show file tree
Hide file tree
Showing 16 changed files with 150 additions and 28 deletions.
5 changes: 5 additions & 0 deletions .changeset/chilled-yaks-beg.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@rocket.chat/meteor": patch
---

Fixed issue in Marketplace that caused a subscription app to show incorrect modals when subscribing
7 changes: 7 additions & 0 deletions .changeset/weak-tigers-suffer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@rocket.chat/meteor": minor
"@rocket.chat/model-typings": minor
"@rocket.chat/rest-typings": minor
---

Added the ability to filter chats by `queued` on the Current Chats Omnichannel page
3 changes: 2 additions & 1 deletion apps/meteor/app/livechat/imports/server/rest/rooms.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ API.v1.addRoute(
async get() {
const { offset, count } = await getPaginationItems(this.queryParams);
const { sort, fields } = await this.parseJsonQuery();
const { agents, departmentId, open, tags, roomName, onhold } = this.queryParams;
const { agents, departmentId, open, tags, roomName, onhold, queued } = this.queryParams;
const { createdAt, customFields, closedAt } = this.queryParams;

const createdAtParam = validateDateParams('createdAt', createdAt);
Expand Down Expand Up @@ -69,6 +69,7 @@ API.v1.addRoute(
tags,
customFields: parsedCf,
onhold,
queued,
options: { offset, count, sort, fields },
}),
);
Expand Down
3 changes: 3 additions & 0 deletions apps/meteor/app/livechat/server/api/lib/rooms.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export async function findRooms({
tags,
customFields,
onhold,
queued,
options: { offset, count, fields, sort },
}: {
agents?: Array<string>;
Expand All @@ -31,6 +32,7 @@ export async function findRooms({
tags?: Array<string>;
customFields?: Record<string, string>;
onhold?: string | boolean;
queued?: string | boolean;
options: { offset: number; count: number; fields: Record<string, number>; sort: Record<string, number> };
}): Promise<PaginatedResult<{ rooms: Array<IOmnichannelRoom> }>> {
const extraQuery = await callbacks.run('livechat.applyRoomRestrictions', {});
Expand All @@ -44,6 +46,7 @@ export async function findRooms({
tags,
customFields,
onhold: ['t', 'true', '1'].includes(`${onhold}`),
queued: ['t', 'true', '1'].includes(`${queued}`),
options: {
sort: sort || { ts: -1 },
offset,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,13 +76,13 @@ const AppStatus = ({ app, showStatus = true, isAppDetailsPage, installed, ...pro
isAppPurchased,
onDismiss: cancelAction,
onSuccess: confirmAction,
setIsPurchased: setPurchased,
});

const handleAcquireApp = useCallback(() => {
setLoading(true);
setPurchased(true);
appInstallationHandler();
}, [appInstallationHandler, setLoading, setPurchased]);
}, [appInstallationHandler, setLoading]);

// @TODO we should refactor this to not use the label to determine the variant
const getStatusVariant = (status: appStatusSpanResponseProps) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,17 @@ export type AppInstallationHandlerParams = {
isAppPurchased?: boolean;
onDismiss: () => void;
onSuccess: (action: Actions | '', appPermissions?: App['permissions']) => void;
setIsPurchased: (purchased: boolean) => void;
};

export function useAppInstallationHandler({ app, action, isAppPurchased, onDismiss, onSuccess }: AppInstallationHandlerParams) {
export function useAppInstallationHandler({
app,
action,
isAppPurchased,
onDismiss,
onSuccess,
setIsPurchased,
}: AppInstallationHandlerParams) {
const dispatchToastMessage = useToastMessageDispatch();
const setModal = useSetModal();

Expand Down Expand Up @@ -62,15 +70,24 @@ export function useAppInstallationHandler({ app, action, isAppPurchased, onDismi
if (action === 'purchase' && !isAppPurchased) {
try {
const data = await appsOrchestrator.buildExternalUrl(app.id, app.purchaseType, false);
setModal(<IframeModal url={data.url} cancel={onDismiss} confirm={openPermissionModal} />);
setModal(
<IframeModal
url={data.url}
cancel={onDismiss}
confirm={() => {
setIsPurchased(true);
openPermissionModal();
}}
/>,
);
} catch (error) {
handleAPIError(error);
}
return;
}

openPermissionModal();
}, [action, isAppPurchased, openPermissionModal, appsOrchestrator, app.id, app.purchaseType, setModal, onDismiss]);
}, [action, isAppPurchased, openPermissionModal, appsOrchestrator, app.id, app.purchaseType, setModal, onDismiss, setIsPurchased]);

return useCallback(async () => {
if (app?.versionIncompatible) {
Expand Down
5 changes: 1 addition & 4 deletions apps/meteor/client/views/marketplace/hooks/useAppMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -91,10 +91,6 @@ export const useAppMenu = (app: App, isAppDetailsPage: boolean) => {
const installationSuccess = useCallback(
async (action: Actions | '', permissionsGranted) => {
if (action) {
if (action === 'purchase') {
setPurchased(true);
}

if (action === 'request') {
setRequestedEndUser(true);
} else {
Expand All @@ -119,6 +115,7 @@ export const useAppMenu = (app: App, isAppDetailsPage: boolean) => {
action,
onDismiss: closeModal,
onSuccess: installationSuccess,
setIsPurchased: setPurchased,
});

const handleAcquireApp = useCallback(() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ type CurrentChatQuery = {
customFields?: string;
sort: string;
count?: number;
queued?: boolean;
};

type useQueryType = (
Expand Down Expand Up @@ -95,8 +96,9 @@ const currentChatQuery: useQueryType = (
}

if (status !== 'all') {
query.open = status === 'opened' || status === 'onhold';
query.open = status === 'opened' || status === 'onhold' || status === 'queued';
query.onhold = status === 'onhold';
query.queued = status === 'queued';
}
if (servedBy && servedBy !== 'all') {
query.agents = [servedBy];
Expand Down Expand Up @@ -170,8 +172,9 @@ const CurrentChatsPage = ({ id, onRowClick }: { id?: string; onRowClick: (_id: s
const renderRow = useCallback(
(room) => {
const { _id, fname, servedBy, ts, lm, department, open, onHold, priorityWeight } = room;
const getStatusText = (open: boolean, onHold: boolean): string => {
const getStatusText = (open: boolean, onHold: boolean, servedBy: boolean): string => {
if (!open) return t('Closed');
if (open && !servedBy) return t('Queued');
return onHold ? t('On_Hold_Chats') : t('Room_Status_Open');
};

Expand All @@ -198,7 +201,7 @@ const CurrentChatsPage = ({ id, onRowClick }: { id?: string; onRowClick: (_id: s
{moment(lm).format('L LTS')}
</GenericTableCell>
<GenericTableCell withTruncatedText data-qa='current-chats-cell-status'>
<RoomActivityIcon room={room} /> {getStatusText(open, onHold)}
<RoomActivityIcon room={room} /> {getStatusText(open, onHold, !!servedBy?.username)}
</GenericTableCell>
{canRemoveClosedChats && !open && <RemoveChatButton _id={_id} />}
</GenericTableRow>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ const FilterByText = ({ setFilter, reload, customFields, setCustomFields, hasCus
['closed', t('Closed')],
['opened', t('Room_Status_Open')],
['onhold', t('On_Hold_Chats')],
['queued', t('Queued')],
];

const [guest, setGuest] = useLocalStorage('guest', '');
Expand Down
12 changes: 12 additions & 0 deletions apps/meteor/server/models/raw/LivechatRooms.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1211,6 +1211,7 @@ export class LivechatRoomsRaw extends BaseRaw<IOmnichannelRoom> implements ILive
visitorId,
roomIds,
onhold,
queued,
options = {},
extraQuery = {},
}: {
Expand All @@ -1226,6 +1227,7 @@ export class LivechatRoomsRaw extends BaseRaw<IOmnichannelRoom> implements ILive
visitorId?: string;
roomIds?: string[];
onhold?: boolean;
queued?: boolean;
options?: { offset?: number; count?: number; sort?: { [k: string]: SortDirection } };
extraQuery?: Filter<IOmnichannelRoom>;
}) {
Expand All @@ -1242,6 +1244,10 @@ export class LivechatRoomsRaw extends BaseRaw<IOmnichannelRoom> implements ILive
...(visitorId && visitorId !== 'undefined' && { 'v._id': visitorId }),
};

if (open) {
query.servedBy = { $exists: true };
}

if (createdAt) {
query.ts = {};
if (createdAt.start) {
Expand Down Expand Up @@ -1280,6 +1286,12 @@ export class LivechatRoomsRaw extends BaseRaw<IOmnichannelRoom> implements ILive
};
}

if (queued) {
query.servedBy = { $exists: false };
query.open = true;
query.onHold = { $ne: true };
}

return this.findPaginated(query, {
sort: options.sort || { name: 1 },
skip: options.offset,
Expand Down
19 changes: 7 additions & 12 deletions apps/meteor/tests/e2e/e2e-encryption.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -601,23 +601,22 @@ test.describe.serial('e2ee room setup', () => {
expect((await api.post('/settings/E2E_Allow_Unencrypted_Messages', { value: false })).status()).toBe(200);
});

test.afterEach(async ({ api }) => {
await api.recreateContext();
});

test('expect save password state on encrypted room', async ({ page }) => {
await page.goto('/account/security');
await poAccountProfile.securityE2EEncryptionSection.click();
await poAccountProfile.securityE2EEncryptionResetKeyButton.click();

await page.locator('role=button[name="Login"]').waitFor();

await page.reload();

await page.locator('role=button[name="Login"]').waitFor();

await injectInitialData();
await restoreState(page, Users.admin);

await page.goto('/home');

await page.locator('role=banner >> text="Save your encryption password"').waitFor();
await expect(page.locator('role=banner >> text="Save your encryption password"')).toBeVisible();

const channelName = faker.string.uuid();
Expand All @@ -631,10 +630,8 @@ test.describe.serial('e2ee room setup', () => {

await poHomeChannel.dismissToast();

await poHomeChannel.content.encryptedRoomHeaderIcon.first().waitFor();
await expect(poHomeChannel.content.encryptedRoomHeaderIcon.first()).toBeVisible();

await page.locator('role=button[name="Save E2EE password"]').waitFor();
await expect(page.locator('role=button[name="Save E2EE password"]')).toBeVisible();

await poHomeChannel.tabs.btnE2EERoomSetupDisableE2E.waitFor();
Expand Down Expand Up @@ -667,8 +664,6 @@ test.describe.serial('e2ee room setup', () => {
// Logout to remove e2ee keys
await poHomeChannel.sidenav.logout();

await page.locator('role=button[name="Login"]').waitFor();
await page.reload();
await page.locator('role=button[name="Login"]').waitFor();

await injectInitialData();
Expand Down Expand Up @@ -788,11 +783,11 @@ test.describe.serial('e2ee support legacy formats', () => {
});

test.afterAll(async ({ api }) => {
expect((await api.post('/settings/E2E_Enable', { value: false })).status()).toBe(200);
expect((await api.post('/settings/E2E_Allow_Unencrypted_Messages', { value: false })).status()).toBe(200);
await api.post('/settings/E2E_Enable', { value: false });
await api.post('/settings/E2E_Allow_Unencrypted_Messages', { value: false });
});

// Not testing upload since it was not implemented in the legacy format
// ->>>>>>>>>>>Not testing upload since it was not implemented in the legacy format
test('expect create a private channel encrypted and send an encrypted message', async ({ page, request }) => {
await page.goto('/home');

Expand Down
4 changes: 2 additions & 2 deletions apps/meteor/tests/e2e/page-objects/fragments/home-content.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,15 +82,15 @@ export class HomeContent {
async sendMessage(text: string): Promise<void> {
await this.joinRoomIfNeeded();
await this.page.waitForSelector('[name="msg"]:not([disabled])');
await this.page.locator('[name="msg"]').type(text);
await this.page.locator('[name="msg"]').fill(text);
await this.page.keyboard.press('Enter');
}

async dispatchSlashCommand(text: string): Promise<void> {
await this.joinRoomIfNeeded();
await this.page.waitForSelector('[name="msg"]:not([disabled])');
await this.page.locator('[name="msg"]').fill('');
await this.page.locator('[name="msg"]').type(text);
await this.page.locator('[name="msg"]').fill(text);
await this.page.keyboard.press('Enter');
await this.page.keyboard.press('Enter');
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,8 +114,8 @@ export class HomeSidenav {
async waitForChannel(): Promise<void> {
await this.page.locator('role=main').waitFor();
await this.page.locator('role=main >> role=heading[level=1]').waitFor();
await this.page.locator('role=main >> role=list').waitFor();

await expect(this.page.locator('role=main >> .rcx-skeleton')).toHaveCount(0);
await expect(this.page.locator('role=main >> role=list')).not.toHaveAttribute('aria-busy', 'true');
}

Expand Down
72 changes: 72 additions & 0 deletions apps/meteor/tests/end-to-end/api/livechat/00-rooms.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import {
fetchMessages,
deleteVisitor,
makeAgentUnavailable,
sendAgentMessage,
} from '../../../data/livechat/rooms';
import { saveTags } from '../../../data/livechat/tags';
import type { DummyResponse } from '../../../data/livechat/utils';
Expand Down Expand Up @@ -341,6 +342,77 @@ describe('LIVECHAT - rooms', () => {
expect(body.rooms.some((room: IOmnichannelRoom) => !!room.closedAt)).to.be.true;
expect(body.rooms.some((room: IOmnichannelRoom) => room.open)).to.be.true;
});
it('should return queued rooms when `queued` param is passed', async () => {
await updateSetting('Livechat_Routing_Method', 'Manual_Selection');
const visitor = await createVisitor();
const room = await createLivechatRoom(visitor.token);

const { body } = await request.get(api('livechat/rooms')).query({ queued: true }).set(credentials).expect(200);

expect(body.rooms.every((room: IOmnichannelRoom) => room.open)).to.be.true;
expect(body.rooms.every((room: IOmnichannelRoom) => !room.servedBy)).to.be.true;
expect(body.rooms.find((froom: IOmnichannelRoom) => froom._id === room._id)).to.be.not.undefined;
});
it('should return queued rooms when `queued` and `open` params are passed', async () => {
const visitor = await createVisitor();
const room = await createLivechatRoom(visitor.token);

const { body } = await request.get(api('livechat/rooms')).query({ queued: true, open: true }).set(credentials).expect(200);

expect(body.rooms.every((room: IOmnichannelRoom) => room.open)).to.be.true;
expect(body.rooms.every((room: IOmnichannelRoom) => !room.servedBy)).to.be.true;
expect(body.rooms.find((froom: IOmnichannelRoom) => froom._id === room._id)).to.be.not.undefined;
});
it('should return open rooms when `open` is param is passed. Open rooms should not include queued conversations', async () => {
const visitor = await createVisitor();
const room = await createLivechatRoom(visitor.token);

const { room: room2 } = await startANewLivechatRoomAndTakeIt();

const { body } = await request.get(api('livechat/rooms')).query({ open: true }).set(credentials).expect(200);

expect(body.rooms.every((room: IOmnichannelRoom) => room.open)).to.be.true;
expect(body.rooms.find((froom: IOmnichannelRoom) => froom._id === room2._id)).to.be.not.undefined;
expect(body.rooms.find((froom: IOmnichannelRoom) => froom._id === room._id)).to.be.undefined;

await updateSetting('Livechat_Routing_Method', 'Auto_Selection');
});
(IS_EE ? describe : describe.skip)('Queued and OnHold chats', () => {
before(async () => {
await updateSetting('Livechat_allow_manual_on_hold', true);
await updateSetting('Livechat_Routing_Method', 'Manual_Selection');
});

after(async () => {
await updateSetting('Livechat_Routing_Method', 'Auto_Selection');
await updateSetting('Livechat_allow_manual_on_hold', false);
});

it('should not return on hold rooms along with queued rooms when `queued` is true and `onHold` is true', async () => {
const { room } = await startANewLivechatRoomAndTakeIt();
await sendAgentMessage(room._id);
const response = await request
.post(api('livechat/room.onHold'))
.set(credentials)
.send({
roomId: room._id,
})
.expect(200);

expect(response.body.success).to.be.true;

const visitor = await createVisitor();
const room2 = await createLivechatRoom(visitor.token);

const { body } = await request.get(api('livechat/rooms')).query({ queued: true, onhold: true }).set(credentials).expect(200);

expect(body.rooms.every((room: IOmnichannelRoom) => room.open)).to.be.true;
expect(body.rooms.every((room: IOmnichannelRoom) => !room.servedBy)).to.be.true;
expect(body.rooms.every((room: IOmnichannelRoom) => !room.onHold)).to.be.true;
expect(body.rooms.find((froom: IOmnichannelRoom) => froom._id === room._id)).to.be.undefined;
expect(body.rooms.find((froom: IOmnichannelRoom) => froom._id === room2._id)).to.be.not.undefined;
});
});
(IS_EE ? it : it.skip)('should return only rooms with the given department', async () => {
const { department } = await createDepartmentWithAnOnlineAgent();

Expand Down
Loading

0 comments on commit 13793dc

Please sign in to comment.