Skip to content

Commit

Permalink
Merge branch 'RocketChat:develop' into feature/add-download-button-to…
Browse files Browse the repository at this point in the history
…-image-preview
  • Loading branch information
abhipatel0211 authored Mar 14, 2024
2 parents a9f681b + e350be5 commit 5206c8d
Show file tree
Hide file tree
Showing 116 changed files with 3,013 additions and 675 deletions.
7 changes: 7 additions & 0 deletions .changeset/afraid-mugs-priest.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@rocket.chat/meteor": patch
"@rocket.chat/core-typings": patch
"@rocket.chat/livechat": patch
---

Fixes issue causing the setDepartment Livechat API overriding some triggers conditions
5 changes: 5 additions & 0 deletions .changeset/bright-cycles-count.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@rocket.chat/meteor": patch
---

fix: Trigger `IPostLivechatRoomStarted` app event after inquiry is created. Previously, this event was fired after a room was created. This allowed to do some actions on rooms, but more elevated actions like transfering a room were not possible as at this point, an inquiry didn't exist.
9 changes: 9 additions & 0 deletions .changeset/chatty-horses-peel.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
"@rocket.chat/meteor": patch
---

**Fixed enterprise settings value not being updated when license is removed or invalid**

Added a license callbacks `onRemoveLicense` and `onInvalidateLicense` to update enterprise settings values when a license is removed/invalid.
This solves a specific scenario where in case of a downgrade (license removal), `settings.get` would continue to return `value` for enterprise settings instead of `invalidValue` as it should.
This would remain the case until the workspace was restarted.
5 changes: 5 additions & 0 deletions .changeset/cmd_preview.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@rocket.chat/meteor": patch
---

Fixed message composer command preview for text type
5 changes: 5 additions & 0 deletions .changeset/empty-hounds-jog.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@rocket.chat/ddp-client": patch
---

fix: livechat sdk reconnect not resubscribing
7 changes: 7 additions & 0 deletions .changeset/rich-rocks-happen.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@rocket.chat/meteor': minor
'@rocket.chat/core-typings': minor
'@rocket.chat/livechat': minor
---

Added new Livechat trigger action "Send message (external service)"
6 changes: 6 additions & 0 deletions .changeset/slimy-clocks-argue.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@rocket.chat/meteor": patch
"@rocket.chat/core-services": patch
---

`stopped` lifecycle method was unexpectedly synchronous when using microservices, causing our code to create race conditions.
5 changes: 5 additions & 0 deletions .changeset/slow-jobs-think.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@rocket.chat/meteor": patch
---

fix: CRM integration mismatch on callback type vs code validation. Previously, callback was attempting to register a `LivechatStarted` event, however, our internal code was expecting a `LivechatStart` event, causing the hook to receive incomplete data
5 changes: 5 additions & 0 deletions .changeset/strange-jars-invent.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@rocket.chat/meteor": patch
---

fix: Validate rooms are not taken before processing by queue. This will prevent an issue that caused a room, that's on an invalid state, to be re-processed by the queue worker, assigning it again to another user despite being already assigned to one. This happens when a room's inquiry gets to an state where it desyncs from the room object. Room is taken & served while inquiry is still queued. This fix will also reconciliate both when something like this happens: whenever the queue picks a chat that's already taken, it will update it's inquiry object to reflect that and avoid processing again.
24 changes: 24 additions & 0 deletions .changeset/stupid-trains-trade.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
---
"@rocket.chat/message-parser": patch
---

feat(message-parser): add timestamps pattern

### Usage

Pattern: <t:{timestamp}:?{format}>

- {timestamp} is a Unix timestamp
- {format} is an optional parameter that can be used to customize the date and time format.

#### Formats

| Format | Description | Example |
| ------ | ------------------------- | --------------------------------------- |
| `t` | Short time | 12:00 AM |
| `T` | Long time | 12:00:00 AM |
| `d` | Short date | 12/31/2020 |
| `D` | Long date | Thursday, December 31, 2020 |
| `f` | Full date and time | Thursday, December 31, 2020 12:00 AM |
| `F` | Full date and time (long) | Thursday, December 31, 2020 12:00:00 AM |
| `R` | Relative time | 1 year ago |
5 changes: 5 additions & 0 deletions .changeset/thin-keys-impress.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@rocket.chat/livechat": patch
---

Fixes issue of the `setDepartment` Livechat API method not setting the store value properly (is was only setting on the guest object)
18 changes: 12 additions & 6 deletions apps/meteor/app/livechat/server/api/lib/livechat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import type {
IOmnichannelRoom,
SelectedAgent,
} from '@rocket.chat/core-typings';
import { License } from '@rocket.chat/license';
import { EmojiCustom, LivechatTrigger, LivechatVisitors, LivechatRooms, LivechatDepartment } from '@rocket.chat/models';
import { Random } from '@rocket.chat/random';
import { Meteor } from 'meteor/meteor';
Expand All @@ -21,12 +22,17 @@ export function online(department: string, skipSettingCheck = false, skipFallbac

async function findTriggers(): Promise<Pick<ILivechatTrigger, '_id' | 'actions' | 'conditions' | 'runOnce'>[]> {
const triggers = await LivechatTrigger.findEnabled().toArray();
return triggers.map(({ _id, actions, conditions, runOnce }) => ({
_id,
actions,
conditions,
runOnce,
}));
const hasLicense = License.hasModule('livechat-enterprise');
const premiumActions = ['use-external-service'];

return triggers
.filter(({ actions }) => hasLicense || actions.some((c) => !premiumActions.includes(c.name)))
.map(({ _id, actions, conditions, runOnce }) => ({
_id,
actions,
conditions,
runOnce,
}));
}

async function findDepartments(
Expand Down
22 changes: 16 additions & 6 deletions apps/meteor/app/livechat/server/hooks/sendToCRM.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,18 +35,28 @@ type OmnichannelRoomWithExtraFields = IOmnichannelRoom & {
oldDepartmentId?: IOmnichannelRoom['departmentId'];
};

type CRMActions =
| 'LivechatSessionStart'
| 'LivechatSessionQueued'
| 'LivechatSession'
| 'LivechatSessionTaken'
| 'LivechatSessionForwarded'
| 'LivechatEdit'
| 'Message'
| 'LeadCapture';

const msgNavType = 'livechat_navigation_history';
const msgClosingType = 'livechat-close';

const isOmnichannelNavigationMessage = (message: IMessage): message is IOmnichannelSystemMessage => {
export const isOmnichannelNavigationMessage = (message: IMessage): message is IOmnichannelSystemMessage => {
return message.t === msgNavType;
};

const isOmnichannelClosingMessage = (message: IMessage): message is IOmnichannelSystemMessage => {
export const isOmnichannelClosingMessage = (message: IMessage): message is IOmnichannelSystemMessage => {
return message.t === msgClosingType;
};

const sendMessageType = (msgType: string): boolean => {
export const sendMessageType = (msgType: string): boolean => {
switch (msgType) {
case msgClosingType:
return true;
Expand All @@ -60,10 +70,10 @@ const sendMessageType = (msgType: string): boolean => {
}
};

const getAdditionalFieldsByType = (type: string, room: OmnichannelRoomWithExtraFields): AdditionalFields => {
export const getAdditionalFieldsByType = (type: CRMActions, room: OmnichannelRoomWithExtraFields): AdditionalFields => {
const { departmentId, servedBy, closedAt, closedBy, closer, oldServedBy, oldDepartmentId } = room;
switch (type) {
case 'LivechatSessionStarted':
case 'LivechatSessionStart':
case 'LivechatSessionQueued':
return { departmentId };
case 'LivechatSession':
Expand All @@ -78,7 +88,7 @@ const getAdditionalFieldsByType = (type: string, room: OmnichannelRoomWithExtraF
};

async function sendToCRM(
type: string,
type: CRMActions,
room: OmnichannelRoomWithExtraFields,
includeMessages: boolean | IOmnichannelSystemMessage[] = true,
): Promise<OmnichannelRoomWithExtraFields> {
Expand Down
1 change: 0 additions & 1 deletion apps/meteor/app/livechat/server/lib/Helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,6 @@ export const createLivechatRoom = async (

const roomId = (await Rooms.insertOne(room)).insertedId;

void Apps.triggerEvent(AppEvents.IPostLivechatRoomStarted, room);
await callbacks.run('livechat.newRoom', room);

await sendMessage(guest, { t: 'livechat-started', msg: '', groupable: false }, room);
Expand Down
2 changes: 2 additions & 0 deletions apps/meteor/app/livechat/server/lib/QueueManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { LivechatInquiry, LivechatRooms, Users } from '@rocket.chat/models';
import { Match, check } from 'meteor/check';
import { Meteor } from 'meteor/meteor';

import { Apps, AppEvents } from '../../../../ee/server/apps';
import { callbacks } from '../../../../lib/callbacks';
import { checkServiceStatus, createLivechatRoom, createLivechatInquiry } from './Helper';
import { RoutingManager } from './RoutingManager';
Expand Down Expand Up @@ -104,6 +105,7 @@ export const QueueManager: queueManager = {
throw new Error('inquiry-not-found');
}

void Apps.triggerEvent(AppEvents.IPostLivechatRoomStarted, room);
await LivechatRooms.updateRoomCount();

await queueInquiry(inquiry, agent);
Expand Down
4 changes: 2 additions & 2 deletions apps/meteor/app/search/server/search.internalService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,10 @@ class Search extends ServiceClassInternal {

const service = new Search();

settings.watch('Search.Provider', () => {
settings.watch('Search.Provider', async () => {
if (searchProviderService.activeProvider?.on) {
api.registerService(service);
} else {
api.destroyService(service);
await api.destroyService(service);
}
});
7 changes: 7 additions & 0 deletions apps/meteor/client/components/GazzodownText.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import type { IRoom } from '@rocket.chat/core-typings';
import { useLocalStorage } from '@rocket.chat/fuselage-hooks';
import type { ChannelMention, UserMention } from '@rocket.chat/gazzodown';
import { MarkupInteractionContext } from '@rocket.chat/gazzodown';
import { escapeRegExp } from '@rocket.chat/string-helpers';
import { useFeaturePreview } from '@rocket.chat/ui-client';
import { useLayout, useRouter, useSetting, useUserPreference, useUserId } from '@rocket.chat/ui-contexts';
import type { UIEvent } from 'react';
import React, { useCallback, memo, useMemo } from 'react';
Expand All @@ -25,6 +27,9 @@ type GazzodownTextProps = {
};

const GazzodownText = ({ mentions, channels, searchText, children }: GazzodownTextProps) => {
const enableTimestamp = useFeaturePreview('enable-timestamp-message-parser');
const [userLanguage] = useLocalStorage('userLanguage', 'en');

const highlights = useMessageListHighlights();
const { triggerProps, openUserCard } = useUserCard();

Expand Down Expand Up @@ -125,6 +130,8 @@ const GazzodownText = ({ mentions, channels, searchText, children }: GazzodownTe
ownUserId,
showMentionSymbol,
triggerProps,
enableTimestamp,
language: userLanguage,
}}
>
{children}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ export const ImageGallery = ({ images, onClose, loadMore }: { images: IUpload[];
const preventPropagation = usePreventPropagation();

return createPortal(
<FocusScope contain restoreFocus autoFocus>
<FocusScope contain autoFocus>
<Box className={swiperStyle}>
<div className='swiper-container' onClick={onClose}>
<ButtonGroup className='rcx-swiper-controls' onClick={preventPropagation}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,11 @@ const getDropdownContainer = (descendant: HTMLElement | null) => {

const useDropdownPosition = (reference: RefObject<HTMLElement>, target: RefObject<HTMLElement>) => {
const innerContainer = getDropdownContainer(reference.current);
const boundingRect = innerContainer.getBoundingClientRect();

const viewHeight = document.body.getBoundingClientRect().height;
const refTop = reference.current?.getBoundingClientRect().top ?? 0;
const targetHeight = target.current?.getBoundingClientRect().height || 0;

const placement = useMemo(() => {
if (boundingRect.top === 0) {
return 'top-start';
}
if (refTop >= viewHeight / 2) {
return 'top-end';
}
Expand All @@ -52,6 +47,10 @@ type EmojiPickerDesktopDropdownProps = {
reference: RefObject<HTMLElement>;
};

/**
* @reference is the trigger element target
* @ref is the dropdown element target
* */
const EmojiPickerDesktopDropdown = forwardRef(function ToolboxDropdownDesktop(
{ reference, children }: EmojiPickerDesktopDropdownProps,
ref: Ref<HTMLElement>,
Expand Down
74 changes: 74 additions & 0 deletions apps/meteor/client/views/omnichannel/triggers/ConditionForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import type { SelectOption } from '@rocket.chat/fuselage';
import { Field, FieldGroup, FieldLabel, FieldRow, NumberInput, Select, TextInput } from '@rocket.chat/fuselage';
import { useUniqueId } from '@rocket.chat/fuselage-hooks';
import { useTranslation } from '@rocket.chat/ui-contexts';
import type { ComponentProps } from 'react';
import React, { useMemo } from 'react';
import type { Control } from 'react-hook-form';
import { Controller, useWatch } from 'react-hook-form';

import type { TriggersPayload } from './EditTrigger';

type ConditionFormType = ComponentProps<typeof Field> & {
index: number;
control: Control<TriggersPayload>;
};

export const ConditionForm = ({ control, index, ...props }: ConditionFormType) => {
const conditionFieldId = useUniqueId();
const t = useTranslation();
const conditionName = useWatch({ control, name: `conditions.${index}.name` });

const placeholders: { [conditionName: string]: string } = useMemo(
() => ({
'page-url': t('Enter_a_regex'),
'time-on-site': t('Time_in_seconds'),
}),
[t],
);

const conditionOptions: SelectOption[] = useMemo(
() => [
['page-url', t('Visitor_page_URL')],
['time-on-site', t('Visitor_time_on_site')],
['chat-opened-by-visitor', t('Chat_opened_by_visitor')],
['after-guest-registration', t('After_guest_registration')],
],
[t],
);

const conditionValuePlaceholder = placeholders[conditionName];

return (
<FieldGroup {...props}>
<Field>
<FieldLabel htmlFor={conditionFieldId}>{t('Condition')}</FieldLabel>
<FieldRow>
<Controller
name={`conditions.${index}.name`}
control={control}
render={({ field }) => (
<Select {...field} id={conditionFieldId} options={conditionOptions} placeholder={t('Select_an_option')} />
)}
/>
</FieldRow>

{conditionValuePlaceholder && (
<FieldRow>
<Controller
name={`conditions.${index}.value`}
control={control}
render={({ field }) => {
if (conditionName === 'time-on-site') {
return <NumberInput {...field} placeholder={conditionValuePlaceholder} />;
}

return <TextInput {...field} placeholder={conditionValuePlaceholder} />;
}}
/>
</FieldRow>
)}
</Field>
</FieldGroup>
);
};
Loading

0 comments on commit 5206c8d

Please sign in to comment.