Add Twenty Shared & Fix profile image rendering (#8841)

PR Summary: 

1. Added `Twenty Shared` Package to centralize utilitiies as mentioned
in #8942
2. Optimization of `getImageAbsoluteURI.ts` to handle edge cases


![image](https://github.com/user-attachments/assets/c72a3061-6eba-46b8-85ac-869f06bf23c0)

---------

Co-authored-by: Antoine Moreaux <moreaux.antoine@gmail.com>
Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
Mohammed Abdul Razak Wahab
2024-12-17 13:54:21 +05:30
committed by GitHub
parent 4e329d08b0
commit 08a9db2df6
39 changed files with 453 additions and 129 deletions

View File

@ -1,6 +1,8 @@
import styled from '@emotion/styled';
import { isNonEmptyString } from '@sniptt/guards';
import { getImageAbsoluteURI } from 'twenty-ui';
import { getImageAbsoluteURI } from 'twenty-shared';
import { REACT_APP_SERVER_BASE_URL } from '~/config';
type LogoProps = {
primaryLogo?: string | null;
@ -46,16 +48,21 @@ const StyledPrimaryLogo = styled.div<{ src: string }>`
export const Logo = (props: LogoProps) => {
const defaultPrimaryLogoUrl = `${window.location.origin}/icons/android/android-launchericon-192-192.png`;
const primaryLogoUrl = getImageAbsoluteURI(
props.primaryLogo ?? defaultPrimaryLogoUrl,
);
const primaryLogoUrl = getImageAbsoluteURI({
imageUrl: props.primaryLogo ?? defaultPrimaryLogoUrl,
baseUrl: REACT_APP_SERVER_BASE_URL,
});
const secondaryLogoUrl = isNonEmptyString(props.secondaryLogo)
? getImageAbsoluteURI(props.secondaryLogo)
? getImageAbsoluteURI({
imageUrl: props.secondaryLogo,
baseUrl: REACT_APP_SERVER_BASE_URL,
})
: null;
return (
<StyledContainer>
<StyledPrimaryLogo src={primaryLogoUrl} />
<StyledPrimaryLogo src={primaryLogoUrl ?? ''} />
{secondaryLogoUrl && (
<StyledSecondaryLogoContainer>
<StyledSecondaryLogo src={secondaryLogoUrl} />

View File

@ -19,7 +19,7 @@ export const useGetPublicWorkspaceDataBySubdomain = () => {
workspacePublicDataState,
);
const { loading } = useGetPublicWorkspaceDataBySubdomainQuery({
const { loading, data, error } = useGetPublicWorkspaceDataBySubdomainQuery({
skip:
(isMultiWorkspaceEnabled && isDefaultDomain) ||
isDefined(workspacePublicData),
@ -38,5 +38,7 @@ export const useGetPublicWorkspaceDataBySubdomain = () => {
return {
loading,
data: data?.getPublicWorkspaceDataBySubdomain,
error,
};
};

View File

@ -8,11 +8,14 @@ import {
NavigationDrawerProps,
} from '@/ui/navigation/navigation-drawer/components/NavigationDrawer';
import { isAdvancedModeEnabledState } from '@/ui/navigation/navigation-drawer/states/isAdvancedModeEnabledState';
import { getImageAbsoluteURI } from 'twenty-shared';
import { REACT_APP_SERVER_BASE_URL } from '~/config';
import { useIsSettingsDrawer } from '@/navigation/hooks/useIsSettingsDrawer';
import { AdvancedSettingsToggle, getImageAbsoluteURI } from 'twenty-ui';
import { MainNavigationDrawerItems } from './MainNavigationDrawerItems';
import { MainNavigationDrawerItems } from '@/navigation/components/MainNavigationDrawerItems';
import { isNonEmptyString } from '@sniptt/guards';
import { AdvancedSettingsToggle } from 'twenty-ui';
export type AppNavigationDrawerProps = {
className?: string;
@ -40,10 +43,12 @@ export const AppNavigationDrawer = ({
),
}
: {
logo:
(currentWorkspace?.logo &&
getImageAbsoluteURI(currentWorkspace.logo)) ??
undefined,
logo: isNonEmptyString(currentWorkspace?.logo)
? getImageAbsoluteURI({
imageUrl: currentWorkspace.logo,
baseUrl: REACT_APP_SERVER_BASE_URL,
})
: undefined,
title: currentWorkspace?.displayName ?? undefined,
children: <MainNavigationDrawerItems />,
footer: <SupportDropdown />,

View File

@ -1,12 +1,12 @@
import { Company } from '@/companies/types/Company';
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
import { getCompanyDomainName } from '@/object-metadata/utils/getCompanyDomainName';
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
import { getImageAbsoluteURI } from 'twenty-shared';
import { REACT_APP_SERVER_BASE_URL } from '~/config';
import { getLogoUrlFromDomainName } from '~/utils';
import { isDefined } from '~/utils/isDefined';
import { Company } from '@/companies/types/Company';
import { getCompanyDomainName } from '@/object-metadata/utils/getCompanyDomainName';
import { getImageAbsoluteURI } from 'twenty-ui';
import { getImageIdentifierFieldValue } from './getImageIdentifierFieldValue';
export const getAvatarUrl = (
@ -26,7 +26,10 @@ export const getAvatarUrl = (
if (objectNameSingular === CoreObjectNameSingular.Person) {
return isDefined(record.avatarUrl)
? getImageAbsoluteURI(record.avatarUrl)
? getImageAbsoluteURI({
imageUrl: record.avatarUrl,
baseUrl: REACT_APP_SERVER_BASE_URL,
})
: '';
}

View File

@ -2,14 +2,9 @@ import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { isNonEmptyString } from '@sniptt/guards';
import React from 'react';
import {
Button,
IconPhotoUp,
IconTrash,
IconUpload,
IconX,
getImageAbsoluteURI,
} from 'twenty-ui';
import { getImageAbsoluteURI } from 'twenty-shared';
import { Button, IconPhotoUp, IconTrash, IconUpload, IconX } from 'twenty-ui';
import { REACT_APP_SERVER_BASE_URL } from '~/config';
import { isDefined } from '~/utils/isDefined';
const StyledContainer = styled.div`
@ -117,7 +112,10 @@ export const ImageInput = ({
};
const pictureURI = isNonEmptyString(picture)
? getImageAbsoluteURI(picture)
? getImageAbsoluteURI({
imageUrl: picture,
baseUrl: REACT_APP_SERVER_BASE_URL,
})
: null;
return (

View File

@ -14,12 +14,13 @@ import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { useState } from 'react';
import { useRecoilState, useRecoilValue } from 'recoil';
import { getImageAbsoluteURI } from 'twenty-shared';
import {
IconChevronDown,
MenuItemSelectAvatar,
UndecoratedLink,
getImageAbsoluteURI,
} from 'twenty-ui';
import { REACT_APP_SERVER_BASE_URL } from '~/config';
const StyledLogo = styled.div<{ logo: string }>`
background: url(${({ logo }) => logo});
@ -102,9 +103,12 @@ export const MultiWorkspaceDropdownButton = ({
isNavigationDrawerExpanded={isNavigationDrawerExpanded}
>
<StyledLogo
logo={getImageAbsoluteURI(
currentWorkspace?.logo ?? DEFAULT_WORKSPACE_LOGO,
)}
logo={
getImageAbsoluteURI({
imageUrl: currentWorkspace?.logo ?? '',
baseUrl: REACT_APP_SERVER_BASE_URL,
}) ?? ''
}
/>
<NavigationDrawerAnimatedCollapseWrapper>
<StyledLabel>{currentWorkspace?.displayName ?? ''}</StyledLabel>
@ -132,9 +136,12 @@ export const MultiWorkspaceDropdownButton = ({
text={workspace.displayName ?? '(No name)'}
avatar={
<StyledLogo
logo={getImageAbsoluteURI(
workspace.logo ?? DEFAULT_WORKSPACE_LOGO,
)}
logo={
getImageAbsoluteURI({
imageUrl: workspace.logo ?? DEFAULT_WORKSPACE_LOGO,
baseUrl: REACT_APP_SERVER_BASE_URL,
}) ?? ''
}
/>
}
selected={currentWorkspace?.id === workspace.id}

View File

@ -1,18 +1,23 @@
import { workspacePublicDataState } from '@/auth/states/workspacePublicDataState';
import { Helmet } from 'react-helmet-async';
import { useRecoilValue } from 'recoil';
import { getImageAbsoluteURI } from 'twenty-ui';
import { getImageAbsoluteURI } from 'twenty-shared';
import { REACT_APP_SERVER_BASE_URL } from '~/config';
export const PageFavicon = () => {
const workspacePublicData = useRecoilValue(workspacePublicDataState);
return (
<Helmet>
{workspacePublicData?.logo && (
<link
rel="icon"
type="image/x-icon"
href={getImageAbsoluteURI(workspacePublicData.logo)}
href={
getImageAbsoluteURI({
imageUrl: workspacePublicData.logo,
baseUrl: REACT_APP_SERVER_BASE_URL,
}) ?? ''
}
/>
)}
</Helmet>

View File

@ -1,6 +1,5 @@
import { useRecoilValue } from 'recoil';
import { workspacePublicDataState } from '@/auth/states/workspacePublicDataState';
import { useEffect } from 'react';
import { isDefined } from '~/utils/isDefined';
import { isMultiWorkspaceEnabledState } from '@/client-config/states/isMultiWorkspaceEnabledState';
@ -9,8 +8,10 @@ import { lastAuthenticatedWorkspaceDomainState } from '@/domain-manager/states/l
import { useReadWorkspaceSubdomainFromCurrentLocation } from '@/domain-manager/hooks/useReadWorkspaceSubdomainFromCurrentLocation';
import { useIsCurrentLocationOnDefaultDomain } from '@/domain-manager/hooks/useIsCurrentLocationOnDefaultDomain';
import { useGetPublicWorkspaceDataBySubdomain } from '@/domain-manager/hooks/useGetPublicWorkspaceDataBySubdomain';
export const WorkspaceProviderEffect = () => {
const workspacePublicData = useRecoilValue(workspacePublicDataState);
const { data: getPublicWorkspaceData } =
useGetPublicWorkspaceDataBySubdomain();
const lastAuthenticatedWorkspaceDomain = useRecoilValue(
lastAuthenticatedWorkspaceDomainState,
@ -26,16 +27,16 @@ export const WorkspaceProviderEffect = () => {
useEffect(() => {
if (
isMultiWorkspaceEnabled &&
isDefined(workspacePublicData?.subdomain) &&
workspacePublicData.subdomain !== workspaceSubdomain
isDefined(getPublicWorkspaceData?.subdomain) &&
getPublicWorkspaceData.subdomain !== workspaceSubdomain
) {
redirectToWorkspaceDomain(workspacePublicData.subdomain);
redirectToWorkspaceDomain(getPublicWorkspaceData.subdomain);
}
}, [
workspaceSubdomain,
isMultiWorkspaceEnabled,
redirectToWorkspaceDomain,
workspacePublicData,
getPublicWorkspaceData,
]);
useEffect(() => {