Skip to content

Commit

Permalink
Merge branch 'develop' into fix/archivedRoomComposer
Browse files Browse the repository at this point in the history
  • Loading branch information
kodiakhq[bot] committed Dec 26, 2023
2 parents 997ff4a + 631f6a4 commit 7abc899
Show file tree
Hide file tree
Showing 19 changed files with 118 additions and 27 deletions.
5 changes: 5 additions & 0 deletions .changeset/chilled-cooks-end.md
@@ -0,0 +1,5 @@
---
"@rocket.chat/meteor": patch
---

Fixed an issue displaying the language selection preference empty when it should display 'Default' on the initial value
5 changes: 5 additions & 0 deletions .changeset/green-turkeys-fry.md
@@ -0,0 +1,5 @@
---
'@rocket.chat/meteor': patch
---

Fixed toolbox sub-menu not being displayed when in smaller resolutions
5 changes: 5 additions & 0 deletions .changeset/late-pots-travel.md
@@ -0,0 +1,5 @@
---
"@rocket.chat/meteor": minor
---

fix: Loading state for `Marketplace` related lists
5 changes: 5 additions & 0 deletions .changeset/thin-socks-brush.md
@@ -0,0 +1,5 @@
---
"@rocket.chat/meteor": patch
---

Removed an old behavior that allowed visitors to be created with an empty token on `livechat/visitor` endpoint.
5 changes: 5 additions & 0 deletions apps/meteor/app/livechat/server/api/v1/visitor.ts
Expand Up @@ -30,6 +30,11 @@ API.v1.addRoute('livechat/visitor', {
});

const { customFields, id, token, name, email, department, phone, username, connectionData } = this.bodyParams.visitor;

if (!token?.trim()) {
throw new Meteor.Error('error-invalid-token', 'Token cannot be empty', { method: 'livechat/visitor' });
}

const guest = {
token,
...(id && { id }),
Expand Down
9 changes: 3 additions & 6 deletions apps/meteor/client/contexts/AppsContext.tsx
Expand Up @@ -5,27 +5,24 @@ import { AsyncStatePhase } from '../lib/asyncState';
import type { App } from '../views/marketplace/types';

export type AppsContextValue = {
installedApps: AsyncState<{ apps: App[] }>;
marketplaceApps: AsyncState<{ apps: App[] }>;
privateApps: AsyncState<{ apps: App[] }>;
installedApps: Omit<AsyncState<{ apps: App[] }>, 'error'>;
marketplaceApps: Omit<AsyncState<{ apps: App[] }>, 'error'>;
privateApps: Omit<AsyncState<{ apps: App[] }>, 'error'>;
reload: () => Promise<void>;
};

export const AppsContext = createContext<AppsContextValue>({
installedApps: {
phase: AsyncStatePhase.LOADING,
value: undefined,
error: undefined,
},
marketplaceApps: {
phase: AsyncStatePhase.LOADING,
value: undefined,
error: undefined,
},
privateApps: {
phase: AsyncStatePhase.LOADING,
value: undefined,
error: undefined,
},
reload: () => Promise.resolve(),
});
23 changes: 20 additions & 3 deletions apps/meteor/client/providers/AppsProvider.tsx
Expand Up @@ -8,12 +8,26 @@ import { AppClientOrchestratorInstance } from '../../ee/client/apps/orchestrator
import { AppsContext } from '../contexts/AppsContext';
import { useIsEnterprise } from '../hooks/useIsEnterprise';
import { useInvalidateLicense } from '../hooks/useLicense';
import type { AsyncState } from '../lib/asyncState';
import { AsyncStatePhase } from '../lib/asyncState';
import { useInvalidateAppsCountQueryCallback } from '../views/marketplace/hooks/useAppsCountQuery';
import type { App } from '../views/marketplace/types';

const sortByName = (apps: App[]): App[] => apps.sort((a, b) => (a.name.toLowerCase() > b.name.toLowerCase() ? 1 : -1));

const getAppState = (
loading: boolean,
apps: App[] | undefined,
): Omit<
AsyncState<{
apps: App[];
}>,
'error'
> => ({
phase: loading ? AsyncStatePhase.LOADING : AsyncStatePhase.RESOLVED,
value: { apps: apps || [] },
});

const AppsProvider: FC = ({ children }) => {
const isAdminUser = usePermission('manage-apps');

Expand Down Expand Up @@ -129,13 +143,16 @@ const AppsProvider: FC = ({ children }) => {
},
);

const [marketplaceAppsData, installedAppsData, privateAppsData] = store.data || [];
const { isLoading } = store;

return (
<AppsContext.Provider
children={children}
value={{
installedApps: { phase: AsyncStatePhase.RESOLVED, value: { apps: store.data?.[1] || [] } },
marketplaceApps: { phase: AsyncStatePhase.RESOLVED, value: { apps: store.data?.[0] || [] } },
privateApps: { phase: AsyncStatePhase.RESOLVED, value: { apps: store.data?.[2] || [] } },
installedApps: getAppState(isLoading, installedAppsData),
marketplaceApps: getAppState(isLoading, marketplaceAppsData),
privateApps: getAppState(isLoading, privateAppsData),
reload: async () => {
await Promise.all([queryClient.invalidateQueries(['marketplace'])]);
},
Expand Down
Expand Up @@ -39,7 +39,7 @@ export type AccountPreferencesData = {
};

export const useAccountPreferencesValues = (): AccountPreferencesData => {
const language = useUserPreference<string>('language');
const language = useUserPreference<string>('language') || '';
const userDontAskAgainList = useUserPreference<{ action: string; label: string }[]>('dontAskAgainList') || [];
const dontAskAgainList = userDontAskAgainList.map(({ action }) => action);
const enableAutoAway = useUserPreference<boolean>('enableAutoAway');
Expand Down
Expand Up @@ -132,16 +132,16 @@ const AppsPageContent = (): ReactElement => {
context,
});

const noInstalledApps = appsResult.phase === AsyncStatePhase.RESOLVED && !isMarketplace && appsResult.value.totalAppsLength === 0;
const noInstalledApps = appsResult.phase === AsyncStatePhase.RESOLVED && !isMarketplace && appsResult.value?.totalAppsLength === 0;

const noMarketplaceOrInstalledAppMatches =
appsResult.phase === AsyncStatePhase.RESOLVED && (isMarketplace || isPremium) && appsResult.value.count === 0;
appsResult.phase === AsyncStatePhase.RESOLVED && (isMarketplace || isPremium) && appsResult.value?.count === 0;

const noInstalledAppMatches =
appsResult.phase === AsyncStatePhase.RESOLVED &&
context === 'installed' &&
appsResult.value.totalAppsLength !== 0 &&
appsResult.value.count === 0;
appsResult.value?.totalAppsLength !== 0 &&
appsResult.value?.count === 0;

const noAppRequests = context === 'requested' && appsResult?.value?.count === 0;

Expand Down Expand Up @@ -194,13 +194,13 @@ const AppsPageContent = (): ReactElement => {
}

if (noMarketplaceOrInstalledAppMatches) {
return <NoMarketplaceOrInstalledAppMatchesEmptyState shouldShowSearchText={appsResult.value.shouldShowSearchText} text={text} />;
return <NoMarketplaceOrInstalledAppMatchesEmptyState shouldShowSearchText={!!appsResult.value?.shouldShowSearchText} text={text} />;
}

if (noInstalledAppMatches) {
return (
<NoInstalledAppMatchesEmptyState
shouldShowSearchText={appsResult.value.shouldShowSearchText}
shouldShowSearchText={!!appsResult.value?.shouldShowSearchText}
text={text}
onButtonClick={handleReturn}
/>
Expand Down
Expand Up @@ -11,7 +11,7 @@ import FeaturedAppsSections from './FeaturedAppsSections';
type AppsPageContentBodyProps = {
isMarketplace: boolean;
isFiltered: boolean;
appsResult: { items: App[] } & { shouldShowSearchText: boolean } & PaginatedResult & { allApps: App[] } & { totalAppsLength: number };
appsResult?: { items: App[] } & { shouldShowSearchText: boolean } & PaginatedResult & { allApps: App[] } & { totalAppsLength: number };
itemsPerPage: 25 | 50 | 100;
current: number;
onSetItemsPerPage: React.Dispatch<React.SetStateAction<25 | 50 | 100>>;
Expand Down Expand Up @@ -43,8 +43,8 @@ const AppsPageContentBody = ({
<Box display='flex' flexDirection='column' overflow='hidden' height='100%' pi={24}>
{noErrorsOcurred && (
<Box overflowY='scroll' height='100%' ref={scrollableRef}>
{isMarketplace && !isFiltered && <FeaturedAppsSections appsListId={appsListId} appsResult={appsResult.allApps || []} />}
<AppsList appsListId={appsListId} apps={appsResult.items || []} title={isMarketplace ? t('All_Apps') : undefined} />
{isMarketplace && !isFiltered && <FeaturedAppsSections appsListId={appsListId} appsResult={appsResult?.allApps || []} />}
<AppsList appsListId={appsListId} apps={appsResult?.items || []} title={isMarketplace ? t('All_Apps') : undefined} />
</Box>
)}
</Box>
Expand Down
5 changes: 3 additions & 2 deletions apps/meteor/client/views/marketplace/hooks/useFilteredApps.ts
Expand Up @@ -39,8 +39,9 @@ export const useFilteredApps = ({
sortingMethod: string;
status: string;
context?: string;
}): AsyncState<
{ items: App[] } & { shouldShowSearchText: boolean } & PaginatedResult & { allApps: App[] } & { totalAppsLength: number }
}): Omit<
AsyncState<{ items: App[] } & { shouldShowSearchText: boolean } & PaginatedResult & { allApps: App[] } & { totalAppsLength: number }>,
'error'
> => {
const value = useMemo(() => {
if (appsData.value === undefined) {
Expand Down
@@ -0,0 +1,36 @@
import { States, StatesIcon, StatesTitle, StatesSubtitle, StatesActions, StatesAction, StatesLink, Box } from '@rocket.chat/fuselage';
import { useRole, useRouter, useTranslation } from '@rocket.chat/ui-contexts';
import React from 'react';

import { Page, PageHeader, PageContent } from '../../../components/Page';

const BusinessHoursDisabledPage = () => {
const t = useTranslation();
const router = useRouter();
const isAdmin = useRole('admin');

return (
<Page>
<PageHeader title={t('Business_Hours')} />
<PageContent>
<Box display='flex' justifyContent='center' height='100%'>
<States>
<StatesIcon name='clock' />
<StatesTitle>{t('Business_hours_is_disabled')}</StatesTitle>
<StatesSubtitle>{t('Business_hours_is_disabled_description')}</StatesSubtitle>
{isAdmin && (
<StatesActions>
<StatesAction onClick={() => router.navigate('/admin/settings/Omnichannel')}>{t('Enable_business_hours')}</StatesAction>
</StatesActions>
)}
<StatesLink target='_blank' href='https://go.rocket.chat/omnichannel-docs'>
{t('Learn_more_about_business_hours')}
</StatesLink>
</States>
</Box>
</PageContent>
</Page>
);
};

export default BusinessHoursDisabledPage;
@@ -1,26 +1,31 @@
import { LivechatBusinessHourTypes } from '@rocket.chat/core-typings';
import { useRouteParameter, useRouter } from '@rocket.chat/ui-contexts';
import { useRouteParameter, useRouter, useSetting } from '@rocket.chat/ui-contexts';
import React, { useEffect } from 'react';

import BusinessHoursDisabledPage from './BusinessHoursDisabledPage';
import BusinessHoursMultiplePage from './BusinessHoursMultiplePage';
import EditBusinessHours from './EditBusinessHours';
import EditBusinessHoursWithData from './EditBusinessHoursWithData';
import { useIsSingleBusinessHours } from './useIsSingleBusinessHours';

const BusinessHoursRouter = () => {
const router = useRouter();
const context = useRouteParameter('context');
const id = useRouteParameter('id');
const type = useRouteParameter('type') as LivechatBusinessHourTypes;
const businessHoursEnabled = useSetting('Livechat_enable_business_hours');
const isSingleBH = useIsSingleBusinessHours();

const router = useRouter();

useEffect(() => {
if (isSingleBH) {
router.navigate('/omnichannel/businessHours/edit/default');
}
}, [isSingleBH, router, context, type]);

if (!businessHoursEnabled) {
return <BusinessHoursDisabledPage />;
}

if (context === 'edit' || isSingleBH) {
return type ? <EditBusinessHoursWithData type={type} id={id} /> : null;
}
Expand Down
Expand Up @@ -172,7 +172,7 @@ const CurrentChatsPage = ({ id, onRowClick }: { id?: string; onRowClick: (_id: s
const { _id, fname, servedBy, ts, lm, department, open, onHold, priorityWeight } = room;
const getStatusText = (open: boolean, onHold: boolean): string => {
if (!open) return t('Closed');
return onHold ? t('On_Hold_Chats') : t('Open');
return onHold ? t('On_Hold_Chats') : t('Room_Status_Open');
};

return (
Expand Down
Expand Up @@ -28,7 +28,7 @@ const FilterByText: FilterByTextType = ({ setFilter, reload, customFields, setCu
const statusOptions: [string, string][] = [
['all', t('All')],
['closed', t('Closed')],
['opened', t('Open')],
['opened', t('Room_Status_Open')],
['onhold', t('On_Hold_Chats')],
];

Expand Down
Expand Up @@ -91,7 +91,7 @@ const RoomToolbox = ({ className }: RoomToolboxProps) => {
{featuredActions.map(mapToToolboxItem)}
{featuredActions.length > 0 && <HeaderToolboxDivider />}
{visibleActions.map(mapToToolboxItem)}
{(normalActions.length > 6 || roomToolboxExpanded) && (
{(normalActions.length > 6 || !roomToolboxExpanded) && (
<GenericMenu title={t('Options')} data-qa-id='ToolBox-Menu' sections={hiddenActions} placement='bottom-end' />
)}
</>
Expand Down
5 changes: 5 additions & 0 deletions apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json
Expand Up @@ -22,6 +22,7 @@
"This_room_encryption_has_been_disabled_by__username_": "This room's encryption has been disabled by {{username}}",
"Third_party_login": "Third-party login",
"Enabled_E2E_Encryption_for_this_room": "enabled E2E Encryption for this room",
"Enable_business_hours": "Enable business hours",
"disabled": "disabled",
"Disabled_E2E_Encryption_for_this_room": "disabled E2E Encryption for this room",
"@username": "@username",
Expand Down Expand Up @@ -839,6 +840,8 @@
"Business_Hour_Removed": "Business Hour Removed",
"Business_Hours": "Business Hours",
"Business_hours_enabled": "Business hours enabled",
"Business_hours_is_disabled": "Business hours is disabled",
"Business_hours_is_disabled_description": "Enable business hours at the workspace admin panel to let customers know when you're available and when can they expect a response.",
"Business_hours_updated": "Business hours updated",
"busy": "busy",
"Busy": "Busy",
Expand Down Expand Up @@ -3047,6 +3050,7 @@
"Lead_capture_phone_regex": "Lead capture phone regex",
"Learn_more": "Learn more",
"Learn_more_about_agents": "Learn more about agents",
"Learn_more_about_business_hours": "Learn more about business hours",
"Learn_more_about_canned_responses": "Learn more about canned responses",
"Learn_more_about_contacts": "Learn more about contacts",
"Learn_more_about_current_chats": "Learn more about current chats",
Expand Down Expand Up @@ -4455,6 +4459,7 @@
"Room_password_changed_successfully": "Room password changed successfully",
"room_removed_read_only": "Room added writing permission by {{user_by}}",
"room_set_read_only": "Room set as Read Only by {{user_by}}",
"Room_Status_Open": "Open",
"room_removed_read_only_permission": "removed read only permission",
"room_set_read_only_permission": "set room to read only",
"Room_topic_changed_successfully": "Room topic changed successfully",
Expand Down
1 change: 1 addition & 0 deletions apps/meteor/packages/rocketchat-i18n/i18n/pt-BR.i18n.json
Expand Up @@ -3686,6 +3686,7 @@
"Room_password_changed_successfully": "A senha da sala foi alterada com sucesso",
"room_removed_read_only": "Permissão de escrita adicionada à sala por {{user_by}}",
"room_set_read_only": "Sala definida como somente leitura por {{user_by}}",
"Room_Status_Open": "Aberto",
"Room_topic_changed_successfully": "Tópico da sala alterado com sucesso",
"Room_type_changed_successfully": "Tipo de sala alterado com sucesso",
"Room_type_of_default_rooms_cant_be_changed": "Esta é uma sala padrão e o tipo não pode ser alterado; consulte o seu administrador.",
Expand Down
4 changes: 4 additions & 0 deletions apps/meteor/tests/end-to-end/api/livechat/09-visitors.ts
Expand Up @@ -44,6 +44,10 @@ describe('LIVECHAT - visitors', function () {
const { body } = await request.post(api('livechat/visitor')).send({ visitor: {} });
expect(body).to.have.property('success', false);
});
it('should fail when token is an empty string', async () => {
const { body } = await request.post(api('livechat/visitor')).send({ visitor: { token: '' } });
expect(body).to.have.property('success', false);
});
it('should create a visitor', async () => {
const { body } = await request.post(api('livechat/visitor')).send({ visitor: { token: 'test' } });
expect(body).to.have.property('success', true);
Expand Down

0 comments on commit 7abc899

Please sign in to comment.