Skip to content

Commit

Permalink
Merge branch 'develop' into chore/voip-tests
Browse files Browse the repository at this point in the history
  • Loading branch information
kodiakhq[bot] authored Oct 24, 2024
2 parents ecada10 + 72182ba commit c18750a
Show file tree
Hide file tree
Showing 37 changed files with 287 additions and 279 deletions.
21 changes: 7 additions & 14 deletions apps/meteor/client/components/FilterByText.tsx
Original file line number Diff line number Diff line change
@@ -1,28 +1,23 @@
import { Box, Icon, TextInput, Margins } from '@rocket.chat/fuselage';
import { useAutoFocus, useMergedRefs } from '@rocket.chat/fuselage-hooks';
import type { ChangeEvent, FormEvent, HTMLAttributes } from 'react';
import React, { forwardRef, memo, useCallback, useState } from 'react';
import type { ChangeEvent, ComponentPropsWithoutRef, FormEvent } from 'react';
import React, { forwardRef, memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next';

type FilterByTextProps = {
onChange: (filter: string) => void;
// TODO: consider changing the type of TextInput's `onChange` to (event: ChangeEvent<HTMLInputElement>) => void
type FilterByTextProps = Omit<ComponentPropsWithoutRef<typeof TextInput>, 'onChange'> & {
shouldAutoFocus?: boolean;
} & Omit<HTMLAttributes<HTMLInputElement>, 'is' | 'onChange'>;
onChange?: (event: ChangeEvent<HTMLInputElement>) => void;
};

const FilterByText = forwardRef<HTMLInputElement, FilterByTextProps>(function FilterByText(
{ placeholder, onChange: setFilter, shouldAutoFocus = false, children, ...props },
{ placeholder, shouldAutoFocus = false, children, ...props },
ref,
) {
const { t } = useTranslation();
const [text, setText] = useState('');
const autoFocusRef = useAutoFocus(shouldAutoFocus);
const mergedRefs = useMergedRefs(ref, autoFocusRef);

const handleInputChange = (event: ChangeEvent<HTMLInputElement>) => {
setText(event.currentTarget.value);
setFilter(event.currentTarget.value);
};

const handleFormSubmit = useCallback((event: FormEvent<HTMLFormElement>) => {
event.preventDefault();
}, []);
Expand All @@ -35,8 +30,6 @@ const FilterByText = forwardRef<HTMLInputElement, FilterByTextProps>(function Fi
placeholder={placeholder ?? t('Search')}
ref={mergedRefs}
addon={<Icon name='magnifier' size='x20' />}
onChange={handleInputChange}
value={text}
flexGrow={2}
minWidth='x220'
aria-label={placeholder ?? t('Search')}
Expand Down
38 changes: 26 additions & 12 deletions apps/meteor/client/contexts/VideoConfContext.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import type { IRoom } from '@rocket.chat/core-typings';
import { createContext, useContext } from 'react';
import type { Subscription } from 'use-subscription';
import { useSubscription } from 'use-subscription';
import { useSyncExternalStore } from 'use-sync-external-store/shim';

import type { DirectCallData, ProviderCapabilities, CallPreferences, VideoConfManager } from '../lib/VideoConfManager';

Expand All @@ -22,11 +21,26 @@ type VideoConfContextValue = {
rejectIncomingCall: (callId: string) => void;
abortCall: () => void;
setPreferences: (prefs: { mic?: boolean; cam?: boolean }) => void;
queryIncomingCalls: Subscription<DirectCallData[]>;
queryRinging: Subscription<boolean>;
queryCalling: Subscription<boolean>;
queryCapabilities: Subscription<ProviderCapabilities>;
queryPreferences: Subscription<CallPreferences>;
queryIncomingCalls: {
subscribe: (cb: () => void) => () => void;
getSnapshot: () => DirectCallData[];
};
queryRinging: {
subscribe: (cb: () => void) => () => void;
getSnapshot: () => boolean;
};
queryCalling: {
subscribe: (cb: () => void) => () => void;
getSnapshot: () => boolean;
};
queryCapabilities: {
subscribe: (cb: () => void) => () => void;
getSnapshot: () => ProviderCapabilities;
};
queryPreferences: {
subscribe: (cb: () => void) => () => void;
getSnapshot: () => CallPreferences;
};
};

export const VideoConfContext = createContext<VideoConfContextValue | undefined>(undefined);
Expand All @@ -49,24 +63,24 @@ export const useVideoConfAbortCall = (): VideoConfContextValue['abortCall'] => u
export const useVideoConfRejectIncomingCall = (): VideoConfContextValue['rejectIncomingCall'] => useVideoConfContext().rejectIncomingCall;
export const useVideoConfIncomingCalls = (): DirectCallData[] => {
const { queryIncomingCalls } = useVideoConfContext();
return useSubscription(queryIncomingCalls);
return useSyncExternalStore(queryIncomingCalls.subscribe, queryIncomingCalls.getSnapshot);
};
export const useVideoConfSetPreferences = (): VideoConfContextValue['setPreferences'] => useVideoConfContext().setPreferences;
export const useVideoConfIsRinging = (): boolean => {
const { queryRinging } = useVideoConfContext();
return useSubscription(queryRinging);
return useSyncExternalStore(queryRinging.subscribe, queryRinging.getSnapshot);
};
export const useVideoConfIsCalling = (): boolean => {
const { queryCalling } = useVideoConfContext();
return useSubscription(queryCalling);
return useSyncExternalStore(queryCalling.subscribe, queryCalling.getSnapshot);
};
export const useVideoConfCapabilities = (): ProviderCapabilities => {
const { queryCapabilities } = useVideoConfContext();
return useSubscription(queryCapabilities);
return useSyncExternalStore(queryCapabilities.subscribe, queryCapabilities.getSnapshot);
};
export const useVideoConfPreferences = (): CallPreferences => {
const { queryPreferences } = useVideoConfContext();
return useSubscription(queryPreferences);
return useSyncExternalStore(queryPreferences.subscribe, queryPreferences.getSnapshot);
};

export const useVideoConfManager = (): typeof VideoConfManager | undefined => useContext(VideoConfContext)?.manager;
16 changes: 10 additions & 6 deletions apps/meteor/client/lib/VideoConfManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,8 @@ export const VideoConfManager = new (class VideoConfManager extends Emitter<Vide

private incomingDirectCalls: Map<string, IncomingDirectCall>;

private directCalls: DirectCallData[] = [];

private dismissedCalls: Set<string>;

private _preferences: CallPreferences;
Expand All @@ -124,6 +126,13 @@ export const VideoConfManager = new (class VideoConfManager extends Emitter<Vide
this.dismissedCalls = new Set<string>();
this._preferences = { mic: true, cam: false };
this._capabilities = {};

this.on('incoming/changed', () => {
this.directCalls = [...this.incomingDirectCalls.values()]
// Filter out any calls that we're in the process of accepting, so they're already hidden from the UI
.filter((call) => !call.acceptTimeout)
.map(({ timeout: _, acceptTimeout: _t, ...call }) => ({ ...call, dismissed: this.isCallDismissed(call.callId) }));
});
}

public isBusy(): boolean {
Expand All @@ -147,12 +156,7 @@ export const VideoConfManager = new (class VideoConfManager extends Emitter<Vide
}

public getIncomingDirectCalls(): DirectCallData[] {
return (
[...this.incomingDirectCalls.values()]
// Filter out any calls that we're in the process of accepting, so they're already hidden from the UI
.filter((call) => !call.acceptTimeout)
.map(({ timeout: _, acceptTimeout: _t, ...call }) => ({ ...call, dismissed: this.isCallDismissed(call.callId) }))
);
return this.directCalls;
}

public async startCall(roomId: IRoom['_id'], title?: string): Promise<void> {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Pagination, States, StatesIcon, StatesActions, StatesAction, StatesTitle } from '@rocket.chat/fuselage';
import { useDebouncedValue } from '@rocket.chat/fuselage-hooks';
import { useTranslation, useEndpoint } from '@rocket.chat/ui-contexts';
import { useQuery } from '@tanstack/react-query';
import React, { useMemo, useState } from 'react';
Expand All @@ -21,13 +22,16 @@ const BusinessHoursTable = () => {

const { current, itemsPerPage, setItemsPerPage: onSetItemsPerPage, setCurrent: onSetCurrent, ...paginationProps } = usePagination();

const query = useMemo(
() => ({
...(itemsPerPage && { count: itemsPerPage }),
...(current && { offset: current }),
name: text,
}),
[itemsPerPage, current, text],
const query = useDebouncedValue(
useMemo(
() => ({
name: text,
...(itemsPerPage && { count: itemsPerPage }),
...(current && { offset: current }),
}),
[text, itemsPerPage, current],
),
500,
);

const getBusinessHours = useEndpoint('GET', '/v1/livechat/business-hours');
Expand All @@ -47,7 +51,7 @@ const BusinessHoursTable = () => {

return (
<>
<FilterByText onChange={setText} />
<FilterByText value={text} onChange={(event) => setText(event.target.value)} />
{isLoading && (
<GenericTable>
<GenericTableHeader>{headers}</GenericTableHeader>
Expand Down
30 changes: 16 additions & 14 deletions apps/meteor/client/omnichannel/monitors/MonitorsTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ const MonitorsTable = () => {

const [text, setText] = useState('');
const [username, setUsername] = useState('');
const debouncedText = useDebouncedValue(text, 500);

const dispatchToastMessage = useToastMessageDispatch();

Expand All @@ -56,19 +55,20 @@ const MonitorsTable = () => {
const { current, itemsPerPage, setItemsPerPage: onSetItemsPerPage, setCurrent: onSetCurrent, ...paginationProps } = pagination;
const { sortBy, sortDirection, setSort } = sort;

const query = useMemo(
() => ({
text: debouncedText,
sort: `{ "${sortBy}": ${sortDirection === 'asc' ? 1 : -1} }`,
...(itemsPerPage && { count: itemsPerPage }),
...(current && { offset: current }),
}),
[debouncedText, itemsPerPage, current, sortBy, sortDirection],
const query = useDebouncedValue(
useMemo(
() => ({
text,
sort: `{ "${sortBy}": ${sortDirection === 'asc' ? 1 : -1} }`,
...(itemsPerPage && { count: itemsPerPage }),
...(current && { offset: current }),
}),
[text, itemsPerPage, current, sortBy, sortDirection],
),
500,
);

const { data, refetch, isLoading, isSuccess, isError } = useQuery(['omnichannel', 'monitors', debouncedText, pagination, sort], () =>
getMonitors(query),
);
const { data, refetch, isLoading, isSuccess, isError } = useQuery(['omnichannel', 'monitors', query], () => getMonitors(query));

const [defaultQuery] = useState(hashQueryKey([query]));
const queryHasChanged = defaultQuery !== hashQueryKey([query]);
Expand Down Expand Up @@ -144,7 +144,9 @@ const MonitorsTable = () => {
</FieldRow>
</Field>
</Box>
{((isSuccess && data?.monitors.length > 0) || queryHasChanged) && <FilterByText onChange={setText} />}
{((isSuccess && data?.monitors.length > 0) || queryHasChanged) && (
<FilterByText value={text} onChange={(event) => setText(event.target.value)} />
)}
{isLoading && (
<GenericTable>
<GenericTableHeader>{headers}</GenericTableHeader>
Expand All @@ -165,7 +167,7 @@ const MonitorsTable = () => {
)}
{isSuccess && data.monitors.length > 0 && (
<>
<GenericTable aria-busy={text !== debouncedText} aria-live='assertive' data-qa-id='manage-monitors-table'>
<GenericTable aria-busy={isLoading} aria-live='assertive' data-qa-id='manage-monitors-table'>
<GenericTableHeader>{headers}</GenericTableHeader>
<GenericTableBody>
{data.monitors?.map((monitor) => (
Expand Down
24 changes: 14 additions & 10 deletions apps/meteor/client/omnichannel/slaPolicies/SlaTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,19 +25,21 @@ const SlaTable = ({ reload }: { reload: MutableRefObject<() => void> }) => {
const router = useRouter();

const [filter, setFilter] = useState('');
const debouncedFilter = useDebouncedValue(filter, 500);

const { current, itemsPerPage, setItemsPerPage: onSetItemsPerPage, setCurrent: onSetCurrent, ...paginationProps } = usePagination();
const { sortBy, sortDirection, setSort } = useSort<'name' | 'description' | 'dueTimeInMinutes'>('name');

const query = useMemo(
() => ({
text: debouncedFilter,
sort: JSON.stringify({ [sortBy]: sortDirection === 'asc' ? 1 : -1 }),
...(itemsPerPage && { count: itemsPerPage }),
...(current && { offset: current }),
}),
[debouncedFilter, itemsPerPage, current, sortBy, sortDirection],
const query = useDebouncedValue(
useMemo(
() => ({
text: filter,
sort: JSON.stringify({ [sortBy]: sortDirection === 'asc' ? 1 : -1 }),
...(itemsPerPage && { count: itemsPerPage }),
...(current && { offset: current }),
}),
[filter, itemsPerPage, current, sortBy, sortDirection],
),
500,
);

const getSlaData = useEndpoint('GET', '/v1/livechat/sla');
Expand Down Expand Up @@ -84,7 +86,9 @@ const SlaTable = ({ reload }: { reload: MutableRefObject<() => void> }) => {

return (
<>
{((isSuccess && data?.sla.length > 0) || queryHasChanged) && <FilterByText onChange={setFilter} />}
{((isSuccess && data?.sla.length > 0) || queryHasChanged) && (
<FilterByText value={filter} onChange={(event) => setFilter(event.target.value)} />
)}
{isLoading && (
<GenericTable>
<GenericTableHeader>{headers}</GenericTableHeader>
Expand Down
26 changes: 15 additions & 11 deletions apps/meteor/client/omnichannel/tags/TagsTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ import { useRemoveTag } from './useRemoveTag';
const TagsTable = () => {
const t = useTranslation();
const [filter, setFilter] = useState('');
const debouncedFilter = useDebouncedValue(filter, 500);
const router = useRouter();

const { current, itemsPerPage, setItemsPerPage: onSetItemsPerPage, setCurrent: onSetCurrent, ...paginationProps } = usePagination();
Expand All @@ -32,15 +31,18 @@ const TagsTable = () => {
const handleAddNew = useMutableCallback(() => router.navigate('/omnichannel/tags/new'));
const handleDeleteTag = useRemoveTag();

const query = useMemo(
() => ({
viewAll: 'true' as const,
text: debouncedFilter,
sort: JSON.stringify({ [sortBy]: sortDirection === 'asc' ? 1 : -1 }),
...(itemsPerPage && { count: itemsPerPage }),
...(current && { offset: current }),
}),
[debouncedFilter, itemsPerPage, current, sortBy, sortDirection],
const query = useDebouncedValue(
useMemo(
() => ({
viewAll: 'true' as const,
text: filter,
sort: JSON.stringify({ [sortBy]: sortDirection === 'asc' ? 1 : -1 }),
...(itemsPerPage && { count: itemsPerPage }),
...(current && { offset: current }),
}),
[filter, itemsPerPage, current, sortBy, sortDirection],
),
500,
);

const getTags = useEndpoint('GET', '/v1/livechat/tags');
Expand Down Expand Up @@ -69,7 +71,9 @@ const TagsTable = () => {

return (
<>
{((isSuccess && data?.tags.length > 0) || queryHasChanged) && <FilterByText onChange={setFilter} />}
{((isSuccess && data?.tags.length > 0) || queryHasChanged) && (
<FilterByText value={filter} onChange={(event) => setFilter(event.target.value)} />
)}
{isLoading && (
<GenericTable>
<GenericTableHeader>{headers}</GenericTableHeader>
Expand Down
26 changes: 15 additions & 11 deletions apps/meteor/client/omnichannel/units/UnitsTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,20 +23,22 @@ import { useRemoveUnit } from './useRemoveUnit';
const UnitsTable = () => {
const { t } = useTranslation();
const [filter, setFilter] = useState('');
const debouncedFilter = useDebouncedValue(filter, 500);
const router = useRouter();

const { current, itemsPerPage, setItemsPerPage: onSetItemsPerPage, setCurrent: onSetCurrent, ...paginationProps } = usePagination();
const { sortBy, sortDirection, setSort } = useSort<'name' | 'visibility'>('name');

const query = useMemo(
() => ({
text: debouncedFilter,
sort: JSON.stringify({ [sortBy]: sortDirection === 'asc' ? 1 : -1 }),
...(itemsPerPage && { count: itemsPerPage }),
...(current && { offset: current }),
}),
[debouncedFilter, itemsPerPage, current, sortBy, sortDirection],
const query = useDebouncedValue(
useMemo(
() => ({
text: filter,
sort: JSON.stringify({ [sortBy]: sortDirection === 'asc' ? 1 : -1 }),
...(itemsPerPage && { count: itemsPerPage }),
...(current && { offset: current }),
}),
[filter, itemsPerPage, current, sortBy, sortDirection],
),
500,
);

const getUnits = useEndpoint('GET', '/v1/livechat/units');
Expand Down Expand Up @@ -69,7 +71,9 @@ const UnitsTable = () => {

return (
<>
{((isSuccess && data?.units.length > 0) || queryHasChanged) && <FilterByText onChange={setFilter} />}
{((isSuccess && data?.units.length > 0) || queryHasChanged) && (
<FilterByText value={filter} onChange={(event) => setFilter(event.target.value)} />
)}
{isLoading && (
<GenericTable aria-busy>
<GenericTableHeader>{headers}</GenericTableHeader>
Expand All @@ -92,7 +96,7 @@ const UnitsTable = () => {
)}
{isSuccess && data?.units.length > 0 && (
<>
<GenericTable aria-busy={filter !== debouncedFilter}>
<GenericTable aria-busy={isLoading}>
<GenericTableHeader>{headers}</GenericTableHeader>
<GenericTableBody>
{data.units.map(({ _id, name, visibility }) => (
Expand Down
Loading

0 comments on commit c18750a

Please sign in to comment.