Skip to content

Commit

Permalink
Merge teams frontend changes to the backend branch (#21190)
Browse files Browse the repository at this point in the history
* add folder structure

* Create ChannelAutoCompleteMultiple component

* Create GenericModal with Variants

* remove enum

* [WIP] Create Team Modal

* add pagination prop to GenericTable

* create teams modal

* remove ChannelAutoCompleteMultiple component

* set description in team

* fix redirect to room

* remove braces from simple string

Co-authored-by: Tasso Evangelista <[email protected]>

* single coat instead bool

* refactor useReactModal

* Move CreateTeam module

* Split TeamNameInput

* Convert BaseAvatar and UserAvatar to TypeScript

* Add UsersInput

* Convert CreateTeamModal to TypeScript

* Upgrade Fuselage

* update generic modal

* Fix team name validation

* Update i18n strings

* Add autofocus

* Pass translation-check

* Fix goToRoomById call

* Fix JSX expression

* [FIX] Unexpected open or close visitor info (#21094)

* fix unexpected open or close visitor info

* fix lgtm bot warning

Co-authored-by: Tasso Evangelista <[email protected]>

Co-authored-by: Tasso Evangelista <[email protected]>
Co-authored-by: Renato Becker <[email protected]>

* Type check useMethod hook

* Type check useEndpoint hook

* Type check useTranslation hook

* [NEW] use _rocketchatLogger in Apps debugLog (#21000)

* Remove redundant polyfill

* [IMPROVE] Improve Apps permission modal (#21193)

* improve modal design

* add en and pt-BR translations

* fix icon size

* [NEW][APPS] Map description as a room value in Apps (#20811)

* map 'description' as a room value

* Update Apps-Engine version

* [FIX] Wrong license seats number administration info panel (#21222)

* Use active users to display on limit of users for the license on info panel

* Print more info about issues with licenses on server

Co-authored-by: dougfabris <[email protected]>
Co-authored-by: gabriellsh <[email protected]>
Co-authored-by: Tiago Evangelista Pinto <[email protected]>
Co-authored-by: Gabriel Henriques <[email protected]>
Co-authored-by: Tasso Evangelista <[email protected]>
Co-authored-by: Renato Becker <[email protected]>
Co-authored-by: meomay503 <[email protected]>
Co-authored-by: Lucas Sartor Chauvin <[email protected]>
Co-authored-by: Rodrigo Nascimento <[email protected]>
  • Loading branch information
10 people authored Mar 22, 2021
1 parent 93be4fe commit a3a540d
Show file tree
Hide file tree
Showing 69 changed files with 1,263 additions and 202 deletions.
1 change: 1 addition & 0 deletions app/apps/server/converters/rooms.js
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ export class AppRoomsConverter {
isWaitingResponse: 'waitingResponse',
isOpen: 'open',
_USERNAMES: '_USERNAMES',
description: 'description',
isDefault: (room) => {
const result = !!room.default;
delete room.default;
Expand Down
3 changes: 1 addition & 2 deletions app/apps/server/orchestrator.js
Original file line number Diff line number Diff line change
Expand Up @@ -115,8 +115,7 @@ export class AppServerOrchestrator {

debugLog(...args) {
if (this.isDebugging()) {
// eslint-disable-next-line
console.log(...args);
this.getRocketChatLogger().debug(...args);
}
}

Expand Down
2 changes: 1 addition & 1 deletion app/threads/client/components/ThreadComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ const subscriptionFields = {};
const useThreadMessage = (tmid: string): IMessage => {
const [message, setMessage] = useState<IMessage>(() => Tracker.nonreactive(() => ChatMessage.findOne({ _id: tmid })));
const getMessage = useEndpoint('GET', 'chat.getMessage');
const getMessageParsed = useCallback<(params: Mongo.Query<IMessage>) => Promise<IMessage>>(async (params) => {
const getMessageParsed = useCallback<(params: Parameters<typeof getMessage>[0]) => Promise<IMessage>>(async (params) => {
const { message } = await getMessage(params);
return {
...message,
Expand Down
6 changes: 1 addition & 5 deletions app/ui-cached-collection/client/models/CachedCollection.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,6 @@ import Notifications from '../../../notifications/client/lib/Notifications';
import { getConfig } from '../../../ui-utils/client/config';
import { callMethod } from '../../../ui-utils/client/lib/callMethod';

const fromEntries = Object.fromEntries || function fromEntries(iterable) {
return [...iterable].reduce((obj, { 0: key, 1: val }) => Object.assign(obj, { [key]: val }), {});
};

const wrap = (fn) => (...args) => new Promise((resolve, reject) => {
fn(...args, (err, result) => {
if (err) {
Expand Down Expand Up @@ -210,7 +206,7 @@ export class CachedCollection extends Emitter {
}
});

this.collection._collection._docs._map = fromEntries(data.records.map((record) => [record._id, record]));
this.collection._collection._docs._map = Object.fromEntries(data.records.map((record) => [record._id, record]));
this.updatedAt = data.updatedAt || this.updatedAt;

Object.values(this.collection._collection.queries).forEach((query) => this.collection._collection._recomputeResults(query));
Expand Down
3 changes: 2 additions & 1 deletion app/ui/client/views/app/room.js
Original file line number Diff line number Diff line change
Expand Up @@ -888,9 +888,10 @@ Meteor.startup(() => {
return;
}

const room = Rooms.findOne({ _id: rid });
let room = Rooms.findOne({ _id: rid }, { fields: { t: 1 } });

if (room?.t === 'l') {
room = Tracker.nonreactive(() => Rooms.findOne({ _id: rid }));
roomTypes.getConfig(room.t).openCustomProfileTab(this, room, room.v.username);
}
});
Expand Down
2 changes: 1 addition & 1 deletion app/videobridge/client/tabBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ addAction('bbb_video', ({ room }) => {
return useMemo(() => (enabled ? {
groups,
id: 'bbb_video',
title: 'BBB Video Call',
title: 'BBB_Video_Call',
icon: 'phone',
template: templateBBB,
order: live ? -1 : 0,
Expand Down
18 changes: 18 additions & 0 deletions client/components/GenericModal.stories.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import React from 'react';

import GenericModal, { GenericModalDoNotAskAgain } from './GenericModal';


export default {
title: 'components/GenericModal',
component: GenericModal,
};

const func = () => null;
const defaultProps = { onClose: func, onConfirm: func, onCancel: func };

export const _default = () => <GenericModal {...defaultProps} />;
export const Danger = () => <GenericModal {...defaultProps} variant='danger' />;
export const Warning = () => <GenericModal {...defaultProps} variant='warning' />;
export const Success = () => <GenericModal {...defaultProps} variant='success' />;
export const WithDontAskAgain = () => <GenericModalDoNotAskAgain dontAskAgain={{ action: '', label: '' }} {...defaultProps} />;
77 changes: 77 additions & 0 deletions client/components/GenericModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { Box, Button, ButtonGroup, Icon, Modal, ButtonProps } from '@rocket.chat/fuselage';
import React, { FC } from 'react';

import { useTranslation } from '../contexts/TranslationContext';
import { withDoNotAskAgain, RequiredModalProps } from './withDoNotAskAgain';

type VariantType = 'danger' | 'warning' | 'info' | 'success';

type GenericModalProps = RequiredModalProps & {
variant?: VariantType;
cancelText?: string;
confirmText?: string;
title?: string;
icon?: string;
onCancel?: () => void;
onClose: () => void;
onConfirm: () => void;
};

const iconMap = {
danger: 'modal-warning',
warning: 'modal-warning',
info: 'info',
success: 'check',
};

const getButtonProps = (variant: VariantType): ButtonProps => {
switch (variant) {
case 'danger':
return { primary: true, danger: true };
case 'warning':
return { primary: true };
default:
return { };
}
};

const GenericModal: FC<GenericModalProps> = ({
variant = 'info',
children,
cancelText,
confirmText,
title,
icon,
onCancel,
onClose,
onConfirm,
dontAskAgain,
...props
}) => {
const t = useTranslation();

return <Modal {...props}>
<Modal.Header>
{icon === null && <Icon color={variant} name={icon ?? iconMap[variant]} size={24}/>}
<Modal.Title>{title ?? t('Are_you_sure')}</Modal.Title>
<Modal.Close onClick={onClose}/>
</Modal.Header>
<Modal.Content fontScale='p1'>
{children}
</Modal.Content>
<Modal.Footer>
<Box display='flex' flexDirection='row' justifyContent='space-between' alignItems='center'>
{dontAskAgain}
<ButtonGroup align='end' flexGrow={1}>
{onCancel && <Button ghost onClick={onCancel}>{cancelText ?? t('Cancel')}</Button>}
<Button {...getButtonProps(variant)} onClick={onConfirm}>{confirmText ?? t('Ok')}</Button>
</ButtonGroup>
</Box>
</Modal.Footer>
</Modal>;
};

// TODO update withDoNotAskAgain to use onConfirm istead of confirm
export const GenericModalDoNotAskAgain = withDoNotAskAgain<GenericModalProps>(({ confirm, ...props }) => <GenericModal onConfirm={confirm} {...props}/>);

export default GenericModal;
5 changes: 3 additions & 2 deletions client/components/GenericTable/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ const GenericTable = ({
results,
setParams = () => { },
total,
pagination = true,
...props
}, ref) => {
const t = useTranslation();
Expand Down Expand Up @@ -69,7 +70,7 @@ const GenericTable = ({
</Table>
</ScrollableContentWrapper>
</Box>
<Pagination
{pagination && <Pagination
divider
current={current}
itemsPerPage={itemsPerPage}
Expand All @@ -78,7 +79,7 @@ const GenericTable = ({
count={total || 0}
onSetItemsPerPage={setItemsPerPage}
onSetCurrent={setCurrent}
/>
/>}
</>
}
</>;
Expand Down
4 changes: 2 additions & 2 deletions client/components/Message/Actions/index.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, { FC } from 'react';
import { IconProps, Icon, Button, ButtonGroup } from '@rocket.chat/fuselage';

import { useTranslation } from '../../../contexts/TranslationContext';
import { TranslationKey, useTranslation } from '../../../contexts/TranslationContext';
import { Content } from '..';

type RunAction = () => void;
Expand All @@ -10,7 +10,7 @@ type ActionOptions = {
mid: string;
id: string;
icon: IconProps['name'];
i18nLabel?: string;
i18nLabel?: TranslationKey;
label?: string;
runAction?: RunAction;
};
Expand Down
14 changes: 0 additions & 14 deletions client/components/avatar/BaseAvatar.js

This file was deleted.

16 changes: 16 additions & 0 deletions client/components/avatar/BaseAvatar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import React, { FC, useState } from 'react';
import { Avatar, AvatarProps, Skeleton } from '@rocket.chat/fuselage';

export type BaseAvatarProps = AvatarProps;

const BaseAvatar: FC<BaseAvatarProps> = ({ size, ...props }) => {
const [error, setError] = useState<unknown>(false);

if (error) {
return <Skeleton variant='rect' {...props} />;
}

return <Avatar onError={setError} size={size} {...props}/>;
};

export default BaseAvatar;
12 changes: 0 additions & 12 deletions client/components/avatar/UserAvatar.js

This file was deleted.

22 changes: 22 additions & 0 deletions client/components/avatar/UserAvatar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import React, { FC, memo } from 'react';

import BaseAvatar, { BaseAvatarProps } from './BaseAvatar';
import { useUserAvatarPath } from '../../contexts/AvatarUrlContext';

type UserAvatarProps = Omit<BaseAvatarProps, 'url' | 'title'> & {
username: string;
etag?: string;
url?: string;
};

const UserAvatar: FC<UserAvatarProps> = ({ username, etag, ...rest }) => {
const getUserAvatarPath = useUserAvatarPath();
const {
url = getUserAvatarPath(username, etag),
...props
} = rest;

return <BaseAvatar url={url} title={username} {...props}/>;
};

export default memo(UserAvatar);
46 changes: 0 additions & 46 deletions client/contexts/ServerContext.ts

This file was deleted.

72 changes: 72 additions & 0 deletions client/contexts/ServerContext/ServerContext.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { createContext, useCallback, useContext, useMemo } from 'react';

import { ServerEndpointMethodOf, ServerEndpointPath, ServerEndpointFunction, ServerEndpointRequestPayload, ServerEndpointFormData, ServerEndpointResponsePayload } from './endpoints';
import { ServerMethodFunction, ServerMethodName, ServerMethodParameters, ServerMethodReturn, ServerMethods } from './methods';

type ServerContextValue = {
info: {};
absoluteUrl: (path: string) => string;
callMethod?: <MethodName extends ServerMethodName>(methodName: MethodName, ...args: ServerMethodParameters<MethodName>) => Promise<ServerMethodReturn<MethodName>>;
callEndpoint?: <
Method extends ServerEndpointMethodOf<Path>,
Path extends ServerEndpointPath
>(httpMethod: Method, endpoint: Path, params: ServerEndpointRequestPayload<Method, Path>, formData?: ServerEndpointFormData<Method, Path>) => Promise<ServerEndpointResponsePayload<Method, Path>>;
uploadToEndpoint: (endpoint: string, params: any, formData: any) => Promise<void>;
getStream: (streamName: string, options?: {}) => <T>(eventName: string, callback: (data: T) => void) => () => void;
};

export const ServerContext = createContext<ServerContextValue>({
info: {},
absoluteUrl: (path) => path,
uploadToEndpoint: async () => undefined,
getStream: () => () => (): void => undefined,
});

export const useServerInformation = (): {} => useContext(ServerContext).info;

export const useAbsoluteUrl = (): ((path: string) => string) => useContext(ServerContext).absoluteUrl;

export const useMethod = <MethodName extends keyof ServerMethods>(
methodName: MethodName,
): ServerMethodFunction<MethodName> => {
const { callMethod } = useContext(ServerContext);

return useCallback(
(...args: ServerMethodParameters<MethodName>) => {
if (!callMethod) {
throw new Error(`cannot use useMethod(${ methodName }) hook without a wrapping ServerContext`);
}

return callMethod(methodName, ...args);
},
[callMethod, methodName],
);
};

export const useEndpoint = <
Method extends ServerEndpointMethodOf<Path>,
Path extends ServerEndpointPath
>(httpMethod: Method, endpoint: Path): ServerEndpointFunction<Method, Path> => {
const { callEndpoint } = useContext(ServerContext);

return useCallback((params: ServerEndpointRequestPayload<Method, Path>, formData?: ServerEndpointFormData<Method, Path>) => {
if (!callEndpoint) {
throw new Error(`cannot use useEndpoint(${ httpMethod }, ${ endpoint }) hook without a wrapping ServerContext`);
}

return callEndpoint(httpMethod, endpoint, params, formData);
}, [callEndpoint, endpoint, httpMethod]);
};

export const useUpload = (endpoint: string): (params: any, formData: any) => Promise<void> => {
const { uploadToEndpoint } = useContext(ServerContext);
return useCallback((params, formData: any) => uploadToEndpoint(endpoint, params, formData), [endpoint, uploadToEndpoint]);
};

export const useStream = (
streamName: string,
options?: {},
): <T>(eventName: string, callback: (data: T) => void) => (() => void) => {
const { getStream } = useContext(ServerContext);
return useMemo(() => getStream(streamName, options), [getStream, streamName, options]);
};
Loading

0 comments on commit a3a540d

Please sign in to comment.