From 6f506d462df2f8b9bdbf5a99bb3667f7fda58858 Mon Sep 17 00:00:00 2001 From: Guilherme Gazzo Date: Wed, 21 Oct 2020 18:22:00 -0300 Subject: [PATCH 1/5] Add avatar provider --- client/channel/UserCard/index.js | 1 - client/components/basic/avatar/RoomAvatar.js | 7 +++--- client/components/basic/avatar/UserAvatar.js | 18 ++++---------- client/contexts/AvatarContext.ts | 18 ++++++++++++++ client/providers/AvatarProvider.tsx | 22 +++++++++++++++++ client/providers/MeteorProvider.js | 26 +++++++++++--------- definition/IUser.ts | 1 + 7 files changed, 65 insertions(+), 28 deletions(-) create mode 100644 client/contexts/AvatarContext.ts create mode 100644 client/providers/AvatarProvider.tsx diff --git a/client/channel/UserCard/index.js b/client/channel/UserCard/index.js index 254a4bccd5cd0..7c32c1afaad1e 100644 --- a/client/channel/UserCard/index.js +++ b/client/channel/UserCard/index.js @@ -35,7 +35,6 @@ const UserCardWithData = ({ username, onClose, target, open, rid }) => { const unsubscribe = subscription.subscribe(() => { onClose && onClose(); - unsubscribe(); }); return () => { diff --git a/client/components/basic/avatar/RoomAvatar.js b/client/components/basic/avatar/RoomAvatar.js index 8ea63ede0334d..dfe0e0d862135 100644 --- a/client/components/basic/avatar/RoomAvatar.js +++ b/client/components/basic/avatar/RoomAvatar.js @@ -1,10 +1,11 @@ import React from 'react'; -import { roomTypes } from '../../../../app/utils/client'; import BaseAvatar from './BaseAvatar'; +import { useRoomAvatarPath } from '../../../contexts/AvatarContext'; -function RoomAvatar({ room: { type, ...room }, ...rest }) { - const { url = roomTypes.getConfig(type).getAvatarPath({ username: room._id, ...room }), ...props } = rest; +function RoomAvatar({ room, ...rest }) { + const getRoomPathAvatar = useRoomAvatarPath(); + const { url = getRoomPathAvatar(room), ...props } = rest; return ; } diff --git a/client/components/basic/avatar/UserAvatar.js b/client/components/basic/avatar/UserAvatar.js index b157944510e1c..f0fbb67cf0738 100644 --- a/client/components/basic/avatar/UserAvatar.js +++ b/client/components/basic/avatar/UserAvatar.js @@ -1,20 +1,12 @@ import React from 'react'; import BaseAvatar from './BaseAvatar'; -import { useSetting } from '../../../contexts/SettingsContext'; +import { useUserAvatarPath } from '../../../contexts/AvatarContext'; -function UserAvatar({ url, username, etag, ...props }) { - // NOW, `username` and `etag` props are enough to determine the whole state of - // this component, but it must be as performatic as possible as it will be - // rendered many times; and some of the state can be derived at the ancestors. - // Ideally, it should be a purely visual component. - const externalProviderUrl = useSetting('Accounts_AvatarExternalProviderUrl'); - - let externalSource = (externalProviderUrl || '').trim().replace(/\/$/, ''); - externalSource = externalSource !== '' && externalSource.replace('{username}', username); - - const avatarUrl = externalSource || url || `/avatar/${ username }${ etag ? `?etag=${ etag }` : '' }`; - return ; +function UserAvatar({ username, etag, ...rest }) { + const getUserAvatarPath = useUserAvatarPath(); + const { url = getUserAvatarPath(username, etag), ...props } = rest; + return ; } export default UserAvatar; diff --git a/client/contexts/AvatarContext.ts b/client/contexts/AvatarContext.ts new file mode 100644 index 0000000000000..3b390734059ed --- /dev/null +++ b/client/contexts/AvatarContext.ts @@ -0,0 +1,18 @@ +import { createContext, useContext } from 'react'; + +const dummy = 'data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs='; + +type AvatarContextValue = { + getUserPathAvatar: (uid: string, etag?: string) => string; + getRoomPathAvatar: (...args: any) => string; +} +const AvatarContextValueDefault: AvatarContextValue = { + getUserPathAvatar: () => dummy, + getRoomPathAvatar: () => dummy, +}; + +export const AvatarContext = createContext(AvatarContextValueDefault); + +export const useRoomAvatarPath = () => useContext(AvatarContext).getRoomPathAvatar; + +export const useUserAvatarPath = () => useContext(AvatarContext).getUserPathAvatar; diff --git a/client/providers/AvatarProvider.tsx b/client/providers/AvatarProvider.tsx new file mode 100644 index 0000000000000..064b7d5040c22 --- /dev/null +++ b/client/providers/AvatarProvider.tsx @@ -0,0 +1,22 @@ +import React, { useMemo, FC } from 'react'; + +import { useSetting } from '../contexts/SettingsContext'; +import { AvatarContext } from '../contexts/AvatarContext'; +import { roomTypes } from '../../app/utils/client'; + +const AvatarProvider: FC = ({ children }) => { + const externalProviderUrl = String(useSetting('Accounts_AvatarExternalProviderUrl') || ''); + + const contextValue = useMemo(() => ({ + getUserPathAvatar: externalProviderUrl + ? (uid: string): string => { + const externalSource = externalProviderUrl.trim().replace(/\/+$/, '').replace('{username}', uid); + return externalSource; + } : (uid: string, etag?: string): string => `/avatar/${ uid }${ etag ? `?etag=${ etag }` : '' }`, + getRoomPathAvatar: ({ type, ...room }: any): string => roomTypes.getConfig(type || room.t).getAvatarPath({ username: room._id, ...room }), + }), [externalProviderUrl]); + + return ; +}; + +export default AvatarProvider; diff --git a/client/providers/MeteorProvider.js b/client/providers/MeteorProvider.js index 475f6cb083fbb..61060bdd6cc12 100644 --- a/client/providers/MeteorProvider.js +++ b/client/providers/MeteorProvider.js @@ -13,6 +13,8 @@ import SidebarProvider from './SidebarProvider'; import ToastMessagesProvider from './ToastMessagesProvider'; import TranslationProvider from './TranslationProvider'; import UserProvider from './UserProvider'; +import AvatarProvider from './AvatarProvider'; + function MeteorProvider({ children }) { return @@ -23,17 +25,19 @@ function MeteorProvider({ children }) { - - - - - - {children} - - - - - + + + + + + + {children} + + + + + + diff --git a/definition/IUser.ts b/definition/IUser.ts index fb6499d1c6267..ee0afcfe93d3f 100644 --- a/definition/IUser.ts +++ b/definition/IUser.ts @@ -84,6 +84,7 @@ export interface IRole { export interface IUser { _id: string; + avatarETag: string; createdAt: Date; roles: string[]; type: string; From 084fea87ba0fd4fb5c3f2fa6aba765c27d58bb3c Mon Sep 17 00:00:00 2001 From: Guilherme Gazzo Date: Wed, 21 Oct 2020 18:31:48 -0300 Subject: [PATCH 2/5] Merge Both --- client/components/basic/avatar/RoomAvatar.js | 2 +- client/components/basic/avatar/UserAvatar.js | 2 +- .../{AvatarContext.ts => AvatarUrlContext.ts} | 8 +++---- client/providers/AvatarProvider.tsx | 22 ------------------ client/providers/AvatarUrlProvider.tsx | 23 +++++++++++++------ client/providers/MeteorProvider.js | 21 ++++++++--------- 6 files changed, 31 insertions(+), 47 deletions(-) rename client/contexts/{AvatarContext.ts => AvatarUrlContext.ts} (51%) delete mode 100644 client/providers/AvatarProvider.tsx diff --git a/client/components/basic/avatar/RoomAvatar.js b/client/components/basic/avatar/RoomAvatar.js index dfe0e0d862135..77858f9bea53a 100644 --- a/client/components/basic/avatar/RoomAvatar.js +++ b/client/components/basic/avatar/RoomAvatar.js @@ -1,7 +1,7 @@ import React from 'react'; import BaseAvatar from './BaseAvatar'; -import { useRoomAvatarPath } from '../../../contexts/AvatarContext'; +import { useRoomAvatarPath } from '../../../contexts/AvatarUrlContext'; function RoomAvatar({ room, ...rest }) { const getRoomPathAvatar = useRoomAvatarPath(); diff --git a/client/components/basic/avatar/UserAvatar.js b/client/components/basic/avatar/UserAvatar.js index f0fbb67cf0738..1b6b694f0bbe9 100644 --- a/client/components/basic/avatar/UserAvatar.js +++ b/client/components/basic/avatar/UserAvatar.js @@ -1,7 +1,7 @@ import React from 'react'; import BaseAvatar from './BaseAvatar'; -import { useUserAvatarPath } from '../../../contexts/AvatarContext'; +import { useUserAvatarPath } from '../../../contexts/AvatarUrlContext'; function UserAvatar({ username, etag, ...rest }) { const getUserAvatarPath = useUserAvatarPath(); diff --git a/client/contexts/AvatarContext.ts b/client/contexts/AvatarUrlContext.ts similarity index 51% rename from client/contexts/AvatarContext.ts rename to client/contexts/AvatarUrlContext.ts index 3b390734059ed..6284cf4f9ddf9 100644 --- a/client/contexts/AvatarContext.ts +++ b/client/contexts/AvatarUrlContext.ts @@ -6,13 +6,13 @@ type AvatarContextValue = { getUserPathAvatar: (uid: string, etag?: string) => string; getRoomPathAvatar: (...args: any) => string; } -const AvatarContextValueDefault: AvatarContextValue = { +const AvatarUrlContextValueDefault: AvatarContextValue = { getUserPathAvatar: () => dummy, getRoomPathAvatar: () => dummy, }; -export const AvatarContext = createContext(AvatarContextValueDefault); +export const AvatarUrlContext = createContext(AvatarUrlContextValueDefault); -export const useRoomAvatarPath = () => useContext(AvatarContext).getRoomPathAvatar; +export const useRoomAvatarPath = () => useContext(AvatarUrlContext).getRoomPathAvatar; -export const useUserAvatarPath = () => useContext(AvatarContext).getUserPathAvatar; +export const useUserAvatarPath = () => useContext(AvatarUrlContext).getUserPathAvatar; diff --git a/client/providers/AvatarProvider.tsx b/client/providers/AvatarProvider.tsx deleted file mode 100644 index 064b7d5040c22..0000000000000 --- a/client/providers/AvatarProvider.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import React, { useMemo, FC } from 'react'; - -import { useSetting } from '../contexts/SettingsContext'; -import { AvatarContext } from '../contexts/AvatarContext'; -import { roomTypes } from '../../app/utils/client'; - -const AvatarProvider: FC = ({ children }) => { - const externalProviderUrl = String(useSetting('Accounts_AvatarExternalProviderUrl') || ''); - - const contextValue = useMemo(() => ({ - getUserPathAvatar: externalProviderUrl - ? (uid: string): string => { - const externalSource = externalProviderUrl.trim().replace(/\/+$/, '').replace('{username}', uid); - return externalSource; - } : (uid: string, etag?: string): string => `/avatar/${ uid }${ etag ? `?etag=${ etag }` : '' }`, - getRoomPathAvatar: ({ type, ...room }: any): string => roomTypes.getConfig(type || room.t).getAvatarPath({ username: room._id, ...room }), - }), [externalProviderUrl]); - - return ; -}; - -export default AvatarProvider; diff --git a/client/providers/AvatarUrlProvider.tsx b/client/providers/AvatarUrlProvider.tsx index d8ab23998194e..23f16368aae1b 100644 --- a/client/providers/AvatarUrlProvider.tsx +++ b/client/providers/AvatarUrlProvider.tsx @@ -1,13 +1,22 @@ -import React, { FC, useMemo } from 'react'; -import { Avatar } from '@rocket.chat/fuselage'; +import React, { useMemo, FC } from 'react'; -// import { baseURI } from '../../app/utils/client/lib/baseuri'; - -// const base = baseURI; +import { useSetting } from '../contexts/SettingsContext'; +import { AvatarUrlContext } from '../contexts/AvatarUrlContext'; +import { roomTypes } from '../../app/utils/client'; const AvatarUrlProvider: FC = ({ children }) => { - const avatarBase = useMemo(() => ({ baseUrl: '' }), []); - return ; + const externalProviderUrl = String(useSetting('Accounts_AvatarExternalProviderUrl') || ''); + + const contextValue = useMemo(() => ({ + getUserPathAvatar: externalProviderUrl + ? (uid: string): string => { + const externalSource = externalProviderUrl.trim().replace(/\/+$/, '').replace('{username}', uid); + return externalSource; + } : (uid: string, etag?: string): string => `/avatar/${ uid }${ etag ? `?etag=${ etag }` : '' }`, + getRoomPathAvatar: ({ type, ...room }: any): string => roomTypes.getConfig(type || room.t).getAvatarPath({ username: room._id, ...room }), + }), [externalProviderUrl]); + + return ; }; export default AvatarUrlProvider; diff --git a/client/providers/MeteorProvider.js b/client/providers/MeteorProvider.js index 61060bdd6cc12..93272a2ef2f42 100644 --- a/client/providers/MeteorProvider.js +++ b/client/providers/MeteorProvider.js @@ -13,7 +13,6 @@ import SidebarProvider from './SidebarProvider'; import ToastMessagesProvider from './ToastMessagesProvider'; import TranslationProvider from './TranslationProvider'; import UserProvider from './UserProvider'; -import AvatarProvider from './AvatarProvider'; function MeteorProvider({ children }) { @@ -25,19 +24,17 @@ function MeteorProvider({ children }) { - + - - - - - {children} - - - - + + + + {children} + + + - + From 39633c52f426faa459e94835633f00a2a54c564a Mon Sep 17 00:00:00 2001 From: Guilherme Gazzo Date: Wed, 21 Oct 2020 19:23:59 -0300 Subject: [PATCH 3/5] CDN and external Providers --- client/providers/AvatarUrlProvider.tsx | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/client/providers/AvatarUrlProvider.tsx b/client/providers/AvatarUrlProvider.tsx index 23f16368aae1b..de7579e0dce35 100644 --- a/client/providers/AvatarUrlProvider.tsx +++ b/client/providers/AvatarUrlProvider.tsx @@ -5,16 +5,22 @@ import { AvatarUrlContext } from '../contexts/AvatarUrlContext'; import { roomTypes } from '../../app/utils/client'; const AvatarUrlProvider: FC = ({ children }) => { + const cdnAvatarUrl = String(useSetting('CDN_PREFIX') || ''); const externalProviderUrl = String(useSetting('Accounts_AvatarExternalProviderUrl') || ''); - + const getUserPathAvatar = (() => { + if(externalProviderUrl) { + return (uid: string) => externalProviderUrl.trim().replace(/\/+$/, '').replace('{username}', uid); + } + if(cdnAvatarUrl){ + return (uid: string, etag?: string) => `${ cdnAvatarUrl }/avatar/${ uid }${ etag ? `?etag=${ etag }` : '' }`) + } + return (uid: string, etag?: string) => `/avatar/${ uid }${ etag ? `?etag=${ etag }` : '' }` + })(); + const contextValue = useMemo(() => ({ - getUserPathAvatar: externalProviderUrl - ? (uid: string): string => { - const externalSource = externalProviderUrl.trim().replace(/\/+$/, '').replace('{username}', uid); - return externalSource; - } : (uid: string, etag?: string): string => `/avatar/${ uid }${ etag ? `?etag=${ etag }` : '' }`, + getUserPathAvatar, getRoomPathAvatar: ({ type, ...room }: any): string => roomTypes.getConfig(type || room.t).getAvatarPath({ username: room._id, ...room }), - }), [externalProviderUrl]); + }), [externalProviderUrl, cdnAvatarUrl]); return ; }; From c21e70f52f78d7e7cf5201930046ae0cb1bdcbe7 Mon Sep 17 00:00:00 2001 From: Guilherme Gazzo Date: Wed, 21 Oct 2020 19:44:03 -0300 Subject: [PATCH 4/5] tslint --- client/providers/AvatarUrlProvider.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/client/providers/AvatarUrlProvider.tsx b/client/providers/AvatarUrlProvider.tsx index de7579e0dce35..c9f35cb90b812 100644 --- a/client/providers/AvatarUrlProvider.tsx +++ b/client/providers/AvatarUrlProvider.tsx @@ -9,12 +9,12 @@ const AvatarUrlProvider: FC = ({ children }) => { const externalProviderUrl = String(useSetting('Accounts_AvatarExternalProviderUrl') || ''); const getUserPathAvatar = (() => { if(externalProviderUrl) { - return (uid: string) => externalProviderUrl.trim().replace(/\/+$/, '').replace('{username}', uid); + return (uid: string): string => externalProviderUrl.trim().replace(/\/+$/, '').replace('{username}', uid); } if(cdnAvatarUrl){ - return (uid: string, etag?: string) => `${ cdnAvatarUrl }/avatar/${ uid }${ etag ? `?etag=${ etag }` : '' }`) + return (uid: string, etag?: string): string => `${ cdnAvatarUrl }/avatar/${ uid }${ etag ? `?etag=${ etag }` : '' }`); } - return (uid: string, etag?: string) => `/avatar/${ uid }${ etag ? `?etag=${ etag }` : '' }` + return (uid: string, etag?: string): string => `/avatar/${ uid }${ etag ? `?etag=${ etag }` : '' }` })(); const contextValue = useMemo(() => ({ From 53af1cbba1fa30bad6e769e0b63d4d4bcd7b40f9 Mon Sep 17 00:00:00 2001 From: Guilherme Gazzo Date: Wed, 21 Oct 2020 20:53:54 -0300 Subject: [PATCH 5/5] Lint --- client/contexts/AvatarUrlContext.ts | 4 ++-- client/providers/AvatarUrlProvider.tsx | 20 +++++++++----------- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/client/contexts/AvatarUrlContext.ts b/client/contexts/AvatarUrlContext.ts index 6284cf4f9ddf9..557aec9e5ce33 100644 --- a/client/contexts/AvatarUrlContext.ts +++ b/client/contexts/AvatarUrlContext.ts @@ -13,6 +13,6 @@ const AvatarUrlContextValueDefault: AvatarContextValue = { export const AvatarUrlContext = createContext(AvatarUrlContextValueDefault); -export const useRoomAvatarPath = () => useContext(AvatarUrlContext).getRoomPathAvatar; +export const useRoomAvatarPath = (): (uid: string, etag?: string) => string => useContext(AvatarUrlContext).getRoomPathAvatar; -export const useUserAvatarPath = () => useContext(AvatarUrlContext).getUserPathAvatar; +export const useUserAvatarPath = (): (...args: any) => string => useContext(AvatarUrlContext).getUserPathAvatar; diff --git a/client/providers/AvatarUrlProvider.tsx b/client/providers/AvatarUrlProvider.tsx index c9f35cb90b812..45223d1d72f28 100644 --- a/client/providers/AvatarUrlProvider.tsx +++ b/client/providers/AvatarUrlProvider.tsx @@ -7,18 +7,16 @@ import { roomTypes } from '../../app/utils/client'; const AvatarUrlProvider: FC = ({ children }) => { const cdnAvatarUrl = String(useSetting('CDN_PREFIX') || ''); const externalProviderUrl = String(useSetting('Accounts_AvatarExternalProviderUrl') || ''); - const getUserPathAvatar = (() => { - if(externalProviderUrl) { - return (uid: string): string => externalProviderUrl.trim().replace(/\/+$/, '').replace('{username}', uid); - } - if(cdnAvatarUrl){ - return (uid: string, etag?: string): string => `${ cdnAvatarUrl }/avatar/${ uid }${ etag ? `?etag=${ etag }` : '' }`); - } - return (uid: string, etag?: string): string => `/avatar/${ uid }${ etag ? `?etag=${ etag }` : '' }` - })(); - const contextValue = useMemo(() => ({ - getUserPathAvatar, + getUserPathAvatar: ((): (uid: string, etag?: string) => string => { + if (externalProviderUrl) { + return (uid: string): string => externalProviderUrl.trim().replace(/\/+$/, '').replace('{username}', uid); + } + if (cdnAvatarUrl) { + return (uid: string, etag?: string): string => `${ cdnAvatarUrl }/avatar/${ uid }${ etag ? `?etag=${ etag }` : '' }`; + } + return (uid: string, etag?: string): string => `/avatar/${ uid }${ etag ? `?etag=${ etag }` : '' }`; + })(), getRoomPathAvatar: ({ type, ...room }: any): string => roomTypes.getConfig(type || room.t).getAvatarPath({ username: room._id, ...room }), }), [externalProviderUrl, cdnAvatarUrl]);