Improve performance of demo workspace - Rename getImageAbsoluteURIOrBase64 function (#6282)
### Description 1. This PR is a continuation of a previous PR: https://github.com/twentyhq/twenty/pull/6201#pullrequestreview-2175601222 2. One test case was removed here: `packages/twenty-front/src/utils/image/__tests__/getImageAbsoluteURI.test.ts` because since we are not handling base64 images anymore, the result is the same of the last test case. Would you rather we update the test instead? ### Refs - #3514 - https://github.com/twentyhq/twenty/pull/6201 ### Demo https://www.loom.com/share/4f32b535c77a4d418e319b095d09452c?sid=df34adf8-b013-44ef-b794-d54846f52d2d Fixes #3514 --------- Co-authored-by: gitstart-twenty <gitstart-twenty@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
00fea17920
commit
fed12ddfcd
@ -9,7 +9,7 @@ import { MainText } from 'src/components/MainText';
|
|||||||
import { Title } from 'src/components/Title';
|
import { Title } from 'src/components/Title';
|
||||||
import { WhatIsTwenty } from 'src/components/WhatIsTwenty';
|
import { WhatIsTwenty } from 'src/components/WhatIsTwenty';
|
||||||
import { capitalize } from 'src/utils/capitalize';
|
import { capitalize } from 'src/utils/capitalize';
|
||||||
import { getImageAbsoluteURIOrBase64 } from 'src/utils/getImageAbsoluteURIOrBase64';
|
import { getImageAbsoluteURI } from 'src/utils/getImageAbsoluteURI';
|
||||||
|
|
||||||
type SendInviteLinkEmailProps = {
|
type SendInviteLinkEmailProps = {
|
||||||
link: string;
|
link: string;
|
||||||
@ -27,7 +27,7 @@ export const SendInviteLinkEmail = ({
|
|||||||
sender,
|
sender,
|
||||||
serverUrl,
|
serverUrl,
|
||||||
}: SendInviteLinkEmailProps) => {
|
}: SendInviteLinkEmailProps) => {
|
||||||
const workspaceLogo = getImageAbsoluteURIOrBase64(workspace.logo, serverUrl);
|
const workspaceLogo = getImageAbsoluteURI(workspace.logo, serverUrl);
|
||||||
return (
|
return (
|
||||||
<BaseEmail width={333}>
|
<BaseEmail width={333}>
|
||||||
<Title value="Join your team on Twenty" />
|
<Title value="Join your team on Twenty" />
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
export const getImageAbsoluteURIOrBase64 = (
|
export const getImageAbsoluteURI = (
|
||||||
imageUrl?: string | null,
|
imageUrl?: string | null,
|
||||||
serverUrl?: string,
|
serverUrl?: string,
|
||||||
) => {
|
) => {
|
||||||
@ -6,7 +6,7 @@ export const getImageAbsoluteURIOrBase64 = (
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (imageUrl?.startsWith('data:') || imageUrl?.startsWith('https:')) {
|
if (imageUrl?.startsWith('https:')) {
|
||||||
return imageUrl;
|
return imageUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -16,7 +16,6 @@ import { Card } from '@/ui/layout/card/components/Card';
|
|||||||
import { CardContent } from '@/ui/layout/card/components/CardContent';
|
import { CardContent } from '@/ui/layout/card/components/CardContent';
|
||||||
import { TimelineCalendarEvent } from '~/generated-metadata/graphql';
|
import { TimelineCalendarEvent } from '~/generated-metadata/graphql';
|
||||||
import { CalendarChannelVisibility } from '~/generated/graphql';
|
import { CalendarChannelVisibility } from '~/generated/graphql';
|
||||||
import { getImageAbsoluteURIOrBase64 } from '~/utils/image/getImageAbsoluteURIOrBase64';
|
|
||||||
import { isDefined } from '~/utils/isDefined';
|
import { isDefined } from '~/utils/isDefined';
|
||||||
|
|
||||||
type CalendarEventRowProps = {
|
type CalendarEventRowProps = {
|
||||||
@ -163,7 +162,7 @@ export const CalendarEventRow = ({
|
|||||||
key={[participant.workspaceMemberId, participant.displayName]
|
key={[participant.workspaceMemberId, participant.displayName]
|
||||||
.filter(isDefined)
|
.filter(isDefined)
|
||||||
.join('-')}
|
.join('-')}
|
||||||
avatarUrl={getImageAbsoluteURIOrBase64(participant.avatarUrl)}
|
avatarUrl={participant.avatarUrl}
|
||||||
placeholder={
|
placeholder={
|
||||||
participant.firstName && participant.lastName
|
participant.firstName && participant.lastName
|
||||||
? `${participant.firstName} ${participant.lastName}`
|
? `${participant.firstName} ${participant.lastName}`
|
||||||
|
|||||||
@ -6,7 +6,6 @@ import {
|
|||||||
beautifyExactDateTime,
|
beautifyExactDateTime,
|
||||||
beautifyPastDateRelativeToNow,
|
beautifyPastDateRelativeToNow,
|
||||||
} from '~/utils/date-utils';
|
} from '~/utils/date-utils';
|
||||||
import { getImageAbsoluteURIOrBase64 } from '~/utils/image/getImageAbsoluteURIOrBase64';
|
|
||||||
|
|
||||||
const StyledContainer = styled.div`
|
const StyledContainer = styled.div`
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@ -60,7 +59,7 @@ export const CommentHeader = ({ comment, actionBar }: CommentHeaderProps) => {
|
|||||||
<StyledContainer>
|
<StyledContainer>
|
||||||
<StyledLeftContainer>
|
<StyledLeftContainer>
|
||||||
<Avatar
|
<Avatar
|
||||||
avatarUrl={getImageAbsoluteURIOrBase64(avatarUrl)}
|
avatarUrl={avatarUrl}
|
||||||
size="md"
|
size="md"
|
||||||
placeholderColorSeed={author?.id}
|
placeholderColorSeed={author?.id}
|
||||||
placeholder={authorName}
|
placeholder={authorName}
|
||||||
|
|||||||
@ -4,7 +4,6 @@ import { Avatar } from 'twenty-ui';
|
|||||||
import { getDisplayNameFromParticipant } from '@/activities/emails/utils/getDisplayNameFromParticipant';
|
import { getDisplayNameFromParticipant } from '@/activities/emails/utils/getDisplayNameFromParticipant';
|
||||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||||
import { RecordChip } from '@/object-record/components/RecordChip';
|
import { RecordChip } from '@/object-record/components/RecordChip';
|
||||||
import { getImageAbsoluteURIOrBase64 } from '~/utils/image/getImageAbsoluteURIOrBase64';
|
|
||||||
|
|
||||||
const StyledAvatar = styled(Avatar)`
|
const StyledAvatar = styled(Avatar)`
|
||||||
margin-right: ${({ theme }) => theme.spacing(1)};
|
margin-right: ${({ theme }) => theme.spacing(1)};
|
||||||
@ -74,7 +73,7 @@ export const ParticipantChip = ({
|
|||||||
) : (
|
) : (
|
||||||
<StyledChip>
|
<StyledChip>
|
||||||
<StyledAvatar
|
<StyledAvatar
|
||||||
avatarUrl={getImageAbsoluteURIOrBase64(avatarUrl)}
|
avatarUrl={avatarUrl}
|
||||||
type="rounded"
|
type="rounded"
|
||||||
placeholder={displayName}
|
placeholder={displayName}
|
||||||
size="sm"
|
size="sm"
|
||||||
|
|||||||
@ -10,7 +10,6 @@ import { CardContent } from '@/ui/layout/card/components/CardContent';
|
|||||||
import { useRightDrawer } from '@/ui/layout/right-drawer/hooks/useRightDrawer';
|
import { useRightDrawer } from '@/ui/layout/right-drawer/hooks/useRightDrawer';
|
||||||
import { MessageChannelVisibility, TimelineThread } from '~/generated/graphql';
|
import { MessageChannelVisibility, TimelineThread } from '~/generated/graphql';
|
||||||
import { formatToHumanReadableDate } from '~/utils/date-utils';
|
import { formatToHumanReadableDate } from '~/utils/date-utils';
|
||||||
import { getImageAbsoluteURIOrBase64 } from '~/utils/image/getImageAbsoluteURIOrBase64';
|
|
||||||
|
|
||||||
const StyledCardContent = styled(CardContent)<{
|
const StyledCardContent = styled(CardContent)<{
|
||||||
visibility: MessageChannelVisibility;
|
visibility: MessageChannelVisibility;
|
||||||
@ -154,24 +153,20 @@ export const EmailThreadPreview = ({
|
|||||||
<StyledHeading unread={!thread.read}>
|
<StyledHeading unread={!thread.read}>
|
||||||
<StyledParticipantsContainer>
|
<StyledParticipantsContainer>
|
||||||
<Avatar
|
<Avatar
|
||||||
avatarUrl={getImageAbsoluteURIOrBase64(
|
avatarUrl={thread?.firstParticipant?.avatarUrl}
|
||||||
thread?.firstParticipant?.avatarUrl,
|
|
||||||
)}
|
|
||||||
placeholder={thread.firstParticipant.displayName}
|
placeholder={thread.firstParticipant.displayName}
|
||||||
type="rounded"
|
type="rounded"
|
||||||
/>
|
/>
|
||||||
{thread?.lastTwoParticipants?.[0] && (
|
{thread?.lastTwoParticipants?.[0] && (
|
||||||
<StyledAvatar
|
<StyledAvatar
|
||||||
avatarUrl={getImageAbsoluteURIOrBase64(
|
avatarUrl={thread.lastTwoParticipants[0].avatarUrl}
|
||||||
thread.lastTwoParticipants[0].avatarUrl,
|
|
||||||
)}
|
|
||||||
placeholder={thread.lastTwoParticipants[0].displayName}
|
placeholder={thread.lastTwoParticipants[0].displayName}
|
||||||
type="rounded"
|
type="rounded"
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{finalDisplayedName && (
|
{finalDisplayedName && (
|
||||||
<StyledAvatar
|
<StyledAvatar
|
||||||
avatarUrl={getImageAbsoluteURIOrBase64(finalAvatarUrl)}
|
avatarUrl={finalAvatarUrl}
|
||||||
placeholder={finalDisplayedName}
|
placeholder={finalDisplayedName}
|
||||||
type="rounded"
|
type="rounded"
|
||||||
color={isCountIcon ? GRAY_SCALE.gray50 : undefined}
|
color={isCountIcon ? GRAY_SCALE.gray50 : undefined}
|
||||||
|
|||||||
@ -11,7 +11,6 @@ import {
|
|||||||
beautifyExactDateTime,
|
beautifyExactDateTime,
|
||||||
beautifyPastDateRelativeToNow,
|
beautifyPastDateRelativeToNow,
|
||||||
} from '~/utils/date-utils';
|
} from '~/utils/date-utils';
|
||||||
import { getImageAbsoluteURIOrBase64 } from '~/utils/image/getImageAbsoluteURIOrBase64';
|
|
||||||
|
|
||||||
const StyledAvatarContainer = styled.div`
|
const StyledAvatarContainer = styled.div`
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@ -154,9 +153,7 @@ export const TimelineActivity = ({
|
|||||||
<StyledTimelineItemContainer>
|
<StyledTimelineItemContainer>
|
||||||
<StyledAvatarContainer>
|
<StyledAvatarContainer>
|
||||||
<Avatar
|
<Avatar
|
||||||
avatarUrl={getImageAbsoluteURIOrBase64(
|
avatarUrl={activityForTimeline.author?.avatarUrl}
|
||||||
activityForTimeline.author?.avatarUrl,
|
|
||||||
)}
|
|
||||||
placeholder={activityForTimeline.author?.name.firstName ?? ''}
|
placeholder={activityForTimeline.author?.name.firstName ?? ''}
|
||||||
size="sm"
|
size="sm"
|
||||||
type="rounded"
|
type="rounded"
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
|
|
||||||
import { getImageAbsoluteURIOrBase64 } from '~/utils/image/getImageAbsoluteURIOrBase64';
|
import { getImageAbsoluteURI } from '~/utils/image/getImageAbsoluteURI';
|
||||||
|
|
||||||
type LogoProps = {
|
type LogoProps = {
|
||||||
workspaceLogo?: string | null;
|
workspaceLogo?: string | null;
|
||||||
@ -58,7 +58,7 @@ export const Logo = ({ workspaceLogo }: LogoProps) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledContainer>
|
<StyledContainer>
|
||||||
<StyledMainLogo logo={getImageAbsoluteURIOrBase64(workspaceLogo)} />
|
<StyledMainLogo logo={getImageAbsoluteURI(workspaceLogo)} />
|
||||||
<StyledTwentyLogoContainer>
|
<StyledTwentyLogoContainer>
|
||||||
<StyledTwentyLogo src="/icons/android/android-launchericon-192-192.png" />
|
<StyledTwentyLogo src="/icons/android/android-launchericon-192-192.png" />
|
||||||
</StyledTwentyLogoContainer>
|
</StyledTwentyLogoContainer>
|
||||||
|
|||||||
@ -10,7 +10,6 @@ import { NavigationDrawerItem } from '@/ui/navigation/navigation-drawer/componen
|
|||||||
import { NavigationDrawerSection } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerSection';
|
import { NavigationDrawerSection } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerSection';
|
||||||
import { NavigationDrawerSectionTitle } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerSectionTitle';
|
import { NavigationDrawerSectionTitle } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerSectionTitle';
|
||||||
import { useNavigationSection } from '@/ui/navigation/navigation-drawer/hooks/useNavigationSection';
|
import { useNavigationSection } from '@/ui/navigation/navigation-drawer/hooks/useNavigationSection';
|
||||||
import { getImageAbsoluteURIOrBase64 } from '~/utils/image/getImageAbsoluteURIOrBase64';
|
|
||||||
|
|
||||||
import { useFavorites } from '../hooks/useFavorites';
|
import { useFavorites } from '../hooks/useFavorites';
|
||||||
|
|
||||||
@ -81,7 +80,7 @@ export const Favorites = () => {
|
|||||||
Icon={() => (
|
Icon={() => (
|
||||||
<StyledAvatar
|
<StyledAvatar
|
||||||
placeholderColorSeed={recordId}
|
placeholderColorSeed={recordId}
|
||||||
avatarUrl={getImageAbsoluteURIOrBase64(avatarUrl)}
|
avatarUrl={avatarUrl}
|
||||||
type={avatarType}
|
type={avatarType}
|
||||||
placeholder={labelIdentifier}
|
placeholder={labelIdentifier}
|
||||||
className="fav-avatar"
|
className="fav-avatar"
|
||||||
|
|||||||
@ -11,7 +11,7 @@ import {
|
|||||||
} from '@/ui/navigation/navigation-drawer/components/NavigationDrawer';
|
} from '@/ui/navigation/navigation-drawer/components/NavigationDrawer';
|
||||||
import { isNavigationDrawerOpenState } from '@/ui/navigation/states/isNavigationDrawerOpenState';
|
import { isNavigationDrawerOpenState } from '@/ui/navigation/states/isNavigationDrawerOpenState';
|
||||||
import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile';
|
import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile';
|
||||||
import { getImageAbsoluteURIOrBase64 } from '~/utils/image/getImageAbsoluteURIOrBase64';
|
import { getImageAbsoluteURI } from '~/utils/image/getImageAbsoluteURI';
|
||||||
|
|
||||||
import { useIsSettingsPage } from '../hooks/useIsSettingsPage';
|
import { useIsSettingsPage } from '../hooks/useIsSettingsPage';
|
||||||
import { currentMobileNavigationDrawerState } from '../states/currentMobileNavigationDrawerState';
|
import { currentMobileNavigationDrawerState } from '../states/currentMobileNavigationDrawerState';
|
||||||
@ -49,7 +49,7 @@ export const AppNavigationDrawer = ({
|
|||||||
: {
|
: {
|
||||||
logo:
|
logo:
|
||||||
(currentWorkspace?.logo &&
|
(currentWorkspace?.logo &&
|
||||||
getImageAbsoluteURIOrBase64(currentWorkspace.logo)) ??
|
getImageAbsoluteURI(currentWorkspace.logo)) ??
|
||||||
undefined,
|
undefined,
|
||||||
title: currentWorkspace?.displayName ?? undefined,
|
title: currentWorkspace?.displayName ?? undefined,
|
||||||
children: <MainNavigationDrawerItems />,
|
children: <MainNavigationDrawerItems />,
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSi
|
|||||||
import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
|
import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
|
||||||
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||||
import { getLogoUrlFromDomainName } from '~/utils';
|
import { getLogoUrlFromDomainName } from '~/utils';
|
||||||
import { getImageAbsoluteURIOrBase64 } from '~/utils/image/getImageAbsoluteURIOrBase64';
|
import { getImageAbsoluteURI } from '~/utils/image/getImageAbsoluteURI';
|
||||||
import { isDefined } from '~/utils/isDefined';
|
import { isDefined } from '~/utils/isDefined';
|
||||||
|
|
||||||
import { getImageIdentifierFieldValue } from './getImageIdentifierFieldValue';
|
import { getImageIdentifierFieldValue } from './getImageIdentifierFieldValue';
|
||||||
@ -21,7 +21,7 @@ export const getAvatarUrl = (
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (objectNameSingular === CoreObjectNameSingular.Person) {
|
if (objectNameSingular === CoreObjectNameSingular.Person) {
|
||||||
return getImageAbsoluteURIOrBase64(record.avatarUrl) ?? '';
|
return getImageAbsoluteURI(record.avatarUrl) ?? '';
|
||||||
}
|
}
|
||||||
|
|
||||||
const imageIdentifierFieldValue = getImageIdentifierFieldValue(
|
const imageIdentifierFieldValue = getImageIdentifierFieldValue(
|
||||||
|
|||||||
@ -1,7 +1,5 @@
|
|||||||
import { AvatarChip, IconComponent } from 'twenty-ui';
|
import { AvatarChip, IconComponent } from 'twenty-ui';
|
||||||
|
|
||||||
import { getImageAbsoluteURIOrBase64 } from '~/utils/image/getImageAbsoluteURIOrBase64';
|
|
||||||
|
|
||||||
import { Filter } from '../types/Filter';
|
import { Filter } from '../types/Filter';
|
||||||
|
|
||||||
type GenericEntityFilterChipProps = {
|
type GenericEntityFilterChipProps = {
|
||||||
@ -17,7 +15,7 @@ export const GenericEntityFilterChip = ({
|
|||||||
placeholderColorSeed={filter.value}
|
placeholderColorSeed={filter.value}
|
||||||
name={filter.displayValue}
|
name={filter.displayValue}
|
||||||
avatarType="rounded"
|
avatarType="rounded"
|
||||||
avatarUrl={getImageAbsoluteURIOrBase64(filter.displayAvatarUrl) || ''}
|
avatarUrl={filter.displayAvatarUrl}
|
||||||
LeftIcon={Icon}
|
LeftIcon={Icon}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -10,7 +10,6 @@ import { SelectableItem } from '@/ui/layout/selectable-list/components/Selectabl
|
|||||||
import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList';
|
import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList';
|
||||||
import { MenuItemMultiSelectAvatar } from '@/ui/navigation/menu-item/components/MenuItemMultiSelectAvatar';
|
import { MenuItemMultiSelectAvatar } from '@/ui/navigation/menu-item/components/MenuItemMultiSelectAvatar';
|
||||||
import { useAvailableScopeIdOrThrow } from '@/ui/utilities/recoil-scope/scopes-internal/hooks/useAvailableScopeId';
|
import { useAvailableScopeIdOrThrow } from '@/ui/utilities/recoil-scope/scopes-internal/hooks/useAvailableScopeId';
|
||||||
import { getImageAbsoluteURIOrBase64 } from '~/utils/image/getImageAbsoluteURIOrBase64';
|
|
||||||
import { isDefined } from '~/utils/isDefined';
|
import { isDefined } from '~/utils/isDefined';
|
||||||
|
|
||||||
export const StyledSelectableItem = styled(SelectableItem)`
|
export const StyledSelectableItem = styled(SelectableItem)`
|
||||||
@ -65,7 +64,7 @@ export const MultipleObjectRecordSelectItem = ({
|
|||||||
selected={selected}
|
selected={selected}
|
||||||
avatar={
|
avatar={
|
||||||
<Avatar
|
<Avatar
|
||||||
avatarUrl={getImageAbsoluteURIOrBase64(recordIdentifier.avatarUrl)}
|
avatarUrl={recordIdentifier.avatarUrl}
|
||||||
placeholderColorSeed={objectRecordId}
|
placeholderColorSeed={objectRecordId}
|
||||||
placeholder={recordIdentifier.name}
|
placeholder={recordIdentifier.name}
|
||||||
size="md"
|
size="md"
|
||||||
|
|||||||
@ -6,7 +6,6 @@ import { EntityForSelect } from '@/object-record/relation-picker/types/EntityFor
|
|||||||
import { SelectableItem } from '@/ui/layout/selectable-list/components/SelectableItem';
|
import { SelectableItem } from '@/ui/layout/selectable-list/components/SelectableItem';
|
||||||
import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList';
|
import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList';
|
||||||
import { MenuItemSelectAvatar } from '@/ui/navigation/menu-item/components/MenuItemSelectAvatar';
|
import { MenuItemSelectAvatar } from '@/ui/navigation/menu-item/components/MenuItemSelectAvatar';
|
||||||
import { getImageAbsoluteURIOrBase64 } from '~/utils/image/getImageAbsoluteURIOrBase64';
|
|
||||||
|
|
||||||
type SelectableMenuItemSelectProps = {
|
type SelectableMenuItemSelectProps = {
|
||||||
entity: EntityForSelect;
|
entity: EntityForSelect;
|
||||||
@ -40,7 +39,7 @@ export const SelectableMenuItemSelect = ({
|
|||||||
hovered={isSelectedItemId}
|
hovered={isSelectedItemId}
|
||||||
avatar={
|
avatar={
|
||||||
<Avatar
|
<Avatar
|
||||||
avatarUrl={getImageAbsoluteURIOrBase64(entity.avatarUrl)}
|
avatarUrl={entity.avatarUrl}
|
||||||
placeholderColorSeed={entity.id}
|
placeholderColorSeed={entity.id}
|
||||||
placeholder={entity.name}
|
placeholder={entity.name}
|
||||||
size="md"
|
size="md"
|
||||||
|
|||||||
@ -6,7 +6,6 @@ import { DropdownMenuSkeletonItem } from '@/ui/input/relation-picker/components/
|
|||||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||||
import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
|
import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
|
||||||
import { MenuItemMultiSelectAvatar } from '@/ui/navigation/menu-item/components/MenuItemMultiSelectAvatar';
|
import { MenuItemMultiSelectAvatar } from '@/ui/navigation/menu-item/components/MenuItemMultiSelectAvatar';
|
||||||
import { getImageAbsoluteURIOrBase64 } from '~/utils/image/getImageAbsoluteURIOrBase64';
|
|
||||||
|
|
||||||
export const MultipleRecordSelectDropdown = ({
|
export const MultipleRecordSelectDropdown = ({
|
||||||
recordsToSelect,
|
recordsToSelect,
|
||||||
@ -69,7 +68,7 @@ export const MultipleRecordSelectDropdown = ({
|
|||||||
}
|
}
|
||||||
avatar={
|
avatar={
|
||||||
<Avatar
|
<Avatar
|
||||||
avatarUrl={getImageAbsoluteURIOrBase64(record.avatarUrl)}
|
avatarUrl={record.avatarUrl}
|
||||||
placeholderColorSeed={record.id}
|
placeholderColorSeed={record.id}
|
||||||
placeholder={record.name}
|
placeholder={record.name}
|
||||||
size="md"
|
size="md"
|
||||||
|
|||||||
@ -6,7 +6,6 @@ import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSi
|
|||||||
import { useUpdateOneRecord } from '@/object-record/hooks/useUpdateOneRecord';
|
import { useUpdateOneRecord } from '@/object-record/hooks/useUpdateOneRecord';
|
||||||
import { ImageInput } from '@/ui/input/components/ImageInput';
|
import { ImageInput } from '@/ui/input/components/ImageInput';
|
||||||
import { useUploadProfilePictureMutation } from '~/generated/graphql';
|
import { useUploadProfilePictureMutation } from '~/generated/graphql';
|
||||||
import { getImageAbsoluteURIOrBase64 } from '~/utils/image/getImageAbsoluteURIOrBase64';
|
|
||||||
import { isDefined } from '~/utils/isDefined';
|
import { isDefined } from '~/utils/isDefined';
|
||||||
import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull';
|
import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull';
|
||||||
|
|
||||||
@ -101,7 +100,7 @@ export const ProfilePictureUploader = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<ImageInput
|
<ImageInput
|
||||||
picture={getImageAbsoluteURIOrBase64(currentWorkspaceMember?.avatarUrl)}
|
picture={currentWorkspaceMember?.avatarUrl}
|
||||||
onUpload={handleUpload}
|
onUpload={handleUpload}
|
||||||
onRemove={handleRemove}
|
onRemove={handleRemove}
|
||||||
onAbort={handleAbort}
|
onAbort={handleAbort}
|
||||||
|
|||||||
@ -6,7 +6,6 @@ import {
|
|||||||
useUpdateWorkspaceMutation,
|
useUpdateWorkspaceMutation,
|
||||||
useUploadWorkspaceLogoMutation,
|
useUploadWorkspaceLogoMutation,
|
||||||
} from '~/generated/graphql';
|
} from '~/generated/graphql';
|
||||||
import { getImageAbsoluteURIOrBase64 } from '~/utils/image/getImageAbsoluteURIOrBase64';
|
|
||||||
import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull';
|
import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull';
|
||||||
|
|
||||||
export const WorkspaceLogoUploader = () => {
|
export const WorkspaceLogoUploader = () => {
|
||||||
@ -57,7 +56,7 @@ export const WorkspaceLogoUploader = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<ImageInput
|
<ImageInput
|
||||||
picture={getImageAbsoluteURIOrBase64(currentWorkspace?.logo)}
|
picture={currentWorkspace?.logo}
|
||||||
onUpload={onUpload}
|
onUpload={onUpload}
|
||||||
onRemove={onRemove}
|
onRemove={onRemove}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -1,9 +1,10 @@
|
|||||||
import React from 'react';
|
|
||||||
import { useTheme } from '@emotion/react';
|
import { useTheme } from '@emotion/react';
|
||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
|
import React, { useMemo } from 'react';
|
||||||
import { IconFileUpload, IconTrash, IconUpload, IconX } from 'twenty-ui';
|
import { IconFileUpload, IconTrash, IconUpload, IconX } from 'twenty-ui';
|
||||||
|
|
||||||
import { Button } from '@/ui/input/button/components/Button';
|
import { Button } from '@/ui/input/button/components/Button';
|
||||||
|
import { getImageAbsoluteURI } from '~/utils/image/getImageAbsoluteURI';
|
||||||
import { isDefined } from '~/utils/isDefined';
|
import { isDefined } from '~/utils/isDefined';
|
||||||
|
|
||||||
const StyledContainer = styled.div`
|
const StyledContainer = styled.div`
|
||||||
@ -105,16 +106,18 @@ export const ImageInput = ({
|
|||||||
hiddenFileInput.current?.click();
|
hiddenFileInput.current?.click();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const pictureURI = useMemo(() => getImageAbsoluteURI(picture), [picture]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledContainer className={className}>
|
<StyledContainer className={className}>
|
||||||
<StyledPicture
|
<StyledPicture
|
||||||
withPicture={!!picture}
|
withPicture={!!pictureURI}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
onClick={onUploadButtonClick}
|
onClick={onUploadButtonClick}
|
||||||
>
|
>
|
||||||
{picture ? (
|
{pictureURI ? (
|
||||||
<img
|
<img
|
||||||
src={picture || '/images/default-profile-picture.png'}
|
src={pictureURI || '/images/default-profile-picture.png'}
|
||||||
alt="profile"
|
alt="profile"
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
@ -139,7 +142,7 @@ export const ImageInput = ({
|
|||||||
onClick={onAbort}
|
onClick={onAbort}
|
||||||
variant="secondary"
|
variant="secondary"
|
||||||
title="Abort"
|
title="Abort"
|
||||||
disabled={!picture || disabled}
|
disabled={!pictureURI || disabled}
|
||||||
fullWidth
|
fullWidth
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
@ -157,7 +160,7 @@ export const ImageInput = ({
|
|||||||
onClick={onRemove}
|
onClick={onRemove}
|
||||||
variant="secondary"
|
variant="secondary"
|
||||||
title="Remove"
|
title="Remove"
|
||||||
disabled={!picture || disabled}
|
disabled={!pictureURI || disabled}
|
||||||
fullWidth
|
fullWidth
|
||||||
/>
|
/>
|
||||||
</StyledButtonContainer>
|
</StyledButtonContainer>
|
||||||
|
|||||||
@ -14,7 +14,7 @@ import { DEFAULT_WORKSPACE_LOGO } from '@/ui/navigation/navigation-drawer/consta
|
|||||||
import { MULTI_WORKSPACE_DROPDOWN_ID } from '@/ui/navigation/navigation-drawer/constants/MulitWorkspaceDropdownId';
|
import { MULTI_WORKSPACE_DROPDOWN_ID } from '@/ui/navigation/navigation-drawer/constants/MulitWorkspaceDropdownId';
|
||||||
import { useWorkspaceSwitching } from '@/ui/navigation/navigation-drawer/hooks/useWorkspaceSwitching';
|
import { useWorkspaceSwitching } from '@/ui/navigation/navigation-drawer/hooks/useWorkspaceSwitching';
|
||||||
import { NavigationDrawerHotKeyScope } from '@/ui/navigation/navigation-drawer/types/NavigationDrawerHotKeyScope';
|
import { NavigationDrawerHotKeyScope } from '@/ui/navigation/navigation-drawer/types/NavigationDrawerHotKeyScope';
|
||||||
import { getImageAbsoluteURIOrBase64 } from '~/utils/image/getImageAbsoluteURIOrBase64';
|
import { getImageAbsoluteURI } from '~/utils/image/getImageAbsoluteURI';
|
||||||
|
|
||||||
const StyledLogo = styled.div<{ logo: string }>`
|
const StyledLogo = styled.div<{ logo: string }>`
|
||||||
background: url(${({ logo }) => logo});
|
background: url(${({ logo }) => logo});
|
||||||
@ -88,7 +88,7 @@ export const MultiWorkspaceDropdownButton = ({
|
|||||||
<StyledContainer>
|
<StyledContainer>
|
||||||
<StyledLogo
|
<StyledLogo
|
||||||
logo={
|
logo={
|
||||||
getImageAbsoluteURIOrBase64(
|
getImageAbsoluteURI(
|
||||||
currentWorkspace?.logo === null
|
currentWorkspace?.logo === null
|
||||||
? DEFAULT_WORKSPACE_LOGO
|
? DEFAULT_WORKSPACE_LOGO
|
||||||
: currentWorkspace?.logo,
|
: currentWorkspace?.logo,
|
||||||
@ -111,7 +111,7 @@ export const MultiWorkspaceDropdownButton = ({
|
|||||||
avatar={
|
avatar={
|
||||||
<StyledLogo
|
<StyledLogo
|
||||||
logo={
|
logo={
|
||||||
getImageAbsoluteURIOrBase64(
|
getImageAbsoluteURI(
|
||||||
workspace.logo === null
|
workspace.logo === null
|
||||||
? DEFAULT_WORKSPACE_LOGO
|
? DEFAULT_WORKSPACE_LOGO
|
||||||
: workspace.logo,
|
: workspace.logo,
|
||||||
|
|||||||
@ -1,2 +1,2 @@
|
|||||||
export const DEFAULT_WORKSPACE_LOGO =
|
export const DEFAULT_WORKSPACE_LOGO =
|
||||||
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAACXBIWXMAAA7EAAAOxAGVKw4bAAACb0lEQVR4nO2VO4taQRTHr3AblbjxEVlwCwVhg7BoqqCIjy/gAyyFWNlYBOxsfH0KuxgQGwXRUkGuL2S7i1barGAgiwbdW93SnGOc4BonPiKahf3DwXFmuP/fPM4ZlvmlTxAhCBdzHnEQWYiv7Mr4C3NeuVYhQYDPzOUUQgDLBQGcLHNhvQK8DACPx8PTxiqVyvISG43GbyaT6Qfpn06n0m63e/tPAPF4vJ1MJu8kEsnWTCkWi1yr1RKGw+GDRqPBOTfr44vFQvD7/Q/lcpmaaVQAr9fLp1IpO22c47hGOBz+MB6PH+Vy+VYDAL8qlUoGtVotzOfzq4MAgsHgE/6KojiQyWR/bKVSqbSszHFM8Pl8z1YK48JsNltCOBwOnrYLO+8AAIjb+nHbycoTiUQfDJ7tFq4YAHiVSmXBxcD41u8flQU8z7fhzO0r83atVns3Go3u9Xr9x0O/RQXo9/tsIBBg6vX606a52Wz+bZ7P5/WwG29gxSJzhKgA6XTaDoFNF+krFAocmC//4yWEcSf2wTm7mCO19xFgSsKOLI16vV7b7XY7mRNoLwA0JymJ5uQIzgIAuX5PzDElT2m+E8BqtQ4ymcx7Yq7T6a6ZE4sKgOadTucaCwkxp1UzlEKh0GDxIXOwDWHAdi6Xe3swQDQa/Q7mywoolUpvsaptymazDWKxmBHTlWXZm405BFZoNpuGgwEmk4mE2SGtVivii4f1AO7J3ZopkQCQj7Ar1FeRChCJRJzVapX6DKNIfSc1Ax+wtQWQ55h6bH8FWDfYV4fO3wlwDr0C/BcADYiTPCxHqIEA2QsCZAkAKnRGkMbKN/sTX5YHPQ1e7SkAAAAASUVORK5CYII=';
|
'https://twentyhq.github.io/placeholder-images/workspaces/twenty-logo.png';
|
||||||
|
|||||||
@ -1,7 +1,5 @@
|
|||||||
import { AvatarChip } from 'twenty-ui';
|
import { AvatarChip } from 'twenty-ui';
|
||||||
|
|
||||||
import { getImageAbsoluteURIOrBase64 } from '~/utils/image/getImageAbsoluteURIOrBase64';
|
|
||||||
|
|
||||||
export type UserChipProps = {
|
export type UserChipProps = {
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
@ -13,6 +11,6 @@ export const UserChip = ({ id, name, avatarUrl }: UserChipProps) => (
|
|||||||
placeholderColorSeed={id}
|
placeholderColorSeed={id}
|
||||||
name={name}
|
name={name}
|
||||||
avatarType="rounded"
|
avatarType="rounded"
|
||||||
avatarUrl={getImageAbsoluteURIOrBase64(avatarUrl) || ''}
|
avatarUrl={avatarUrl}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -2,7 +2,6 @@ import styled from '@emotion/styled';
|
|||||||
import { Avatar, OverflowingTextWithTooltip } from 'twenty-ui';
|
import { Avatar, OverflowingTextWithTooltip } from 'twenty-ui';
|
||||||
|
|
||||||
import { WorkspaceMember } from '@/workspace-member/types/WorkspaceMember';
|
import { WorkspaceMember } from '@/workspace-member/types/WorkspaceMember';
|
||||||
import { getImageAbsoluteURIOrBase64 } from '~/utils/image/getImageAbsoluteURIOrBase64';
|
|
||||||
|
|
||||||
const StyledContainer = styled.div`
|
const StyledContainer = styled.div`
|
||||||
background: ${({ theme }) => theme.background.secondary};
|
background: ${({ theme }) => theme.background.secondary};
|
||||||
@ -39,7 +38,7 @@ export const WorkspaceMemberCard = ({
|
|||||||
}: WorkspaceMemberCardProps) => (
|
}: WorkspaceMemberCardProps) => (
|
||||||
<StyledContainer>
|
<StyledContainer>
|
||||||
<Avatar
|
<Avatar
|
||||||
avatarUrl={getImageAbsoluteURIOrBase64(workspaceMember.avatarUrl)}
|
avatarUrl={workspaceMember.avatarUrl}
|
||||||
placeholderColorSeed={workspaceMember.id}
|
placeholderColorSeed={workspaceMember.id}
|
||||||
placeholder={workspaceMember.name.firstName || ''}
|
placeholder={workspaceMember.name.firstName || ''}
|
||||||
type="squared"
|
type="squared"
|
||||||
|
|||||||
@ -0,0 +1,21 @@
|
|||||||
|
import { getImageAbsoluteURI } from '../getImageAbsoluteURI';
|
||||||
|
|
||||||
|
describe('getImageAbsoluteURI', () => {
|
||||||
|
it('should return null if imageUrl is null', () => {
|
||||||
|
const imageUrl = null;
|
||||||
|
const result = getImageAbsoluteURI(imageUrl);
|
||||||
|
expect(result).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return absolute url if the imageUrl is an absolute url', () => {
|
||||||
|
const imageUrl = 'https://XXX';
|
||||||
|
const result = getImageAbsoluteURI(imageUrl);
|
||||||
|
expect(result).toBe(imageUrl);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return fully formed url if imageUrl is a relative url', () => {
|
||||||
|
const imageUrl = 'XXX';
|
||||||
|
const result = getImageAbsoluteURI(imageUrl);
|
||||||
|
expect(result).toBe('http://localhost:3000/files/XXX');
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -1,27 +0,0 @@
|
|||||||
import { getImageAbsoluteURIOrBase64 } from '../getImageAbsoluteURIOrBase64';
|
|
||||||
|
|
||||||
describe('getImageAbsoluteURIOrBase64', () => {
|
|
||||||
it('should return null if imageUrl is null', () => {
|
|
||||||
const imageUrl = null;
|
|
||||||
const result = getImageAbsoluteURIOrBase64(imageUrl);
|
|
||||||
expect(result).toBeNull();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should return base64 encoded string if prefixed with data', () => {
|
|
||||||
const imageUrl = 'data:XXX';
|
|
||||||
const result = getImageAbsoluteURIOrBase64(imageUrl);
|
|
||||||
expect(result).toBe(imageUrl);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should return absolute url if the imageUrl is an absolute url', () => {
|
|
||||||
const imageUrl = 'https://XXX';
|
|
||||||
const result = getImageAbsoluteURIOrBase64(imageUrl);
|
|
||||||
expect(result).toBe(imageUrl);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should return fully formed url if imageUrl is a relative url', () => {
|
|
||||||
const imageUrl = 'XXX';
|
|
||||||
const result = getImageAbsoluteURIOrBase64(imageUrl);
|
|
||||||
expect(result).toBe('http://localhost:3000/files/XXX');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@ -1,11 +1,11 @@
|
|||||||
import { REACT_APP_SERVER_BASE_URL } from '~/config';
|
import { REACT_APP_SERVER_BASE_URL } from '~/config';
|
||||||
|
|
||||||
export const getImageAbsoluteURIOrBase64 = (imageUrl?: string | null) => {
|
export const getImageAbsoluteURI = (imageUrl?: string | null) => {
|
||||||
if (!imageUrl) {
|
if (!imageUrl) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (imageUrl?.startsWith('data:') || imageUrl?.startsWith('https:')) {
|
if (imageUrl?.startsWith('https:')) {
|
||||||
return imageUrl;
|
return imageUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -1,6 +1,6 @@
|
|||||||
import { styled } from '@linaria/react';
|
import { styled } from '@linaria/react';
|
||||||
import { isNonEmptyString, isUndefined } from '@sniptt/guards';
|
import { isNonEmptyString, isUndefined } from '@sniptt/guards';
|
||||||
import { useContext } from 'react';
|
import { useContext, useMemo } from 'react';
|
||||||
import { useRecoilState } from 'recoil';
|
import { useRecoilState } from 'recoil';
|
||||||
|
|
||||||
import { invalidAvatarUrlsState } from '@ui/display/avatar/components/states/isInvalidAvatarUrlState';
|
import { invalidAvatarUrlsState } from '@ui/display/avatar/components/states/isInvalidAvatarUrlState';
|
||||||
@ -8,7 +8,7 @@ import { AVATAR_PROPERTIES_BY_SIZE } from '@ui/display/avatar/constants/AvatarPr
|
|||||||
import { AvatarSize } from '@ui/display/avatar/types/AvatarSize';
|
import { AvatarSize } from '@ui/display/avatar/types/AvatarSize';
|
||||||
import { AvatarType } from '@ui/display/avatar/types/AvatarType';
|
import { AvatarType } from '@ui/display/avatar/types/AvatarType';
|
||||||
import { ThemeContext } from '@ui/theme';
|
import { ThemeContext } from '@ui/theme';
|
||||||
import { Nullable, stringToHslColor } from '@ui/utilities';
|
import { Nullable, getImageAbsoluteURI, stringToHslColor } from '@ui/utilities';
|
||||||
|
|
||||||
const StyledAvatar = styled.div<{
|
const StyledAvatar = styled.div<{
|
||||||
size: AvatarSize;
|
size: AvatarSize;
|
||||||
@ -73,15 +73,21 @@ export const Avatar = ({
|
|||||||
invalidAvatarUrlsState,
|
invalidAvatarUrlsState,
|
||||||
);
|
);
|
||||||
|
|
||||||
const noAvatarUrl = !isNonEmptyString(avatarUrl);
|
const avatarImageURI = useMemo(
|
||||||
|
() => getImageAbsoluteURI(avatarUrl),
|
||||||
|
[avatarUrl],
|
||||||
|
);
|
||||||
|
|
||||||
|
const noAvatarUrl = !isNonEmptyString(avatarImageURI);
|
||||||
|
|
||||||
const placeholderChar = placeholder?.[0]?.toLocaleUpperCase();
|
const placeholderChar = placeholder?.[0]?.toLocaleUpperCase();
|
||||||
|
|
||||||
const showPlaceholder = noAvatarUrl || invalidAvatarUrls.includes(avatarUrl);
|
const showPlaceholder =
|
||||||
|
noAvatarUrl || invalidAvatarUrls.includes(avatarImageURI);
|
||||||
|
|
||||||
const handleImageError = () => {
|
const handleImageError = () => {
|
||||||
if (isNonEmptyString(avatarUrl)) {
|
if (isNonEmptyString(avatarImageURI)) {
|
||||||
setInvalidAvatarUrls((prev) => [...prev, avatarUrl]);
|
setInvalidAvatarUrls((prev) => [...prev, avatarImageURI]);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -105,7 +111,7 @@ export const Avatar = ({
|
|||||||
{showPlaceholder ? (
|
{showPlaceholder ? (
|
||||||
placeholderChar
|
placeholderChar
|
||||||
) : (
|
) : (
|
||||||
<StyledImage src={avatarUrl} onError={handleImageError} alt="" />
|
<StyledImage src={avatarImageURI} onError={handleImageError} alt="" />
|
||||||
)}
|
)}
|
||||||
</StyledAvatar>
|
</StyledAvatar>
|
||||||
);
|
);
|
||||||
|
|||||||
30
packages/twenty-ui/src/utilities/config/index.ts
Normal file
30
packages/twenty-ui/src/utilities/config/index.ts
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
declare global {
|
||||||
|
interface Window {
|
||||||
|
_env_?: Record<string, string>;
|
||||||
|
__APOLLO_CLIENT__?: any;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const getDefaultUrl = () => {
|
||||||
|
if (
|
||||||
|
window.location.hostname === 'localhost' ||
|
||||||
|
window.location.hostname === '127.0.0.1'
|
||||||
|
) {
|
||||||
|
// In development environment front and backend usually run on separate ports
|
||||||
|
// we set the default value to localhost:3000.
|
||||||
|
// It dev context, we use env vars to overwrite it
|
||||||
|
return 'http://localhost:3000';
|
||||||
|
} else {
|
||||||
|
// Outside of localhost we assume that they run on the same port
|
||||||
|
// because the backend will serve the frontend
|
||||||
|
// It prod context, we use env-config.js + window var to ovewrite it
|
||||||
|
return `${window.location.protocol}//${window.location.hostname}${
|
||||||
|
window.location.port ? `:${window.location.port}` : ''
|
||||||
|
}`;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const REACT_APP_SERVER_BASE_URL =
|
||||||
|
window._env_?.REACT_APP_SERVER_BASE_URL ||
|
||||||
|
process.env.REACT_APP_SERVER_BASE_URL ||
|
||||||
|
getDefaultUrl();
|
||||||
@ -0,0 +1,15 @@
|
|||||||
|
import { REACT_APP_SERVER_BASE_URL } from '@ui/utilities/config';
|
||||||
|
|
||||||
|
export const getImageAbsoluteURI = (imageUrl?: string | null) => {
|
||||||
|
if (!imageUrl) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (imageUrl?.startsWith('https:')) {
|
||||||
|
return imageUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
const serverFilesUrl = REACT_APP_SERVER_BASE_URL;
|
||||||
|
|
||||||
|
return `${serverFilesUrl}/files/${imageUrl}`;
|
||||||
|
};
|
||||||
@ -1,4 +1,5 @@
|
|||||||
export * from './color/utils/stringToHslColor';
|
export * from './color/utils/stringToHslColor';
|
||||||
|
export * from './image/getImageAbsoluteURI';
|
||||||
export * from './isDefined';
|
export * from './isDefined';
|
||||||
export * from './state/utils/createState';
|
export * from './state/utils/createState';
|
||||||
export * from './types/Nullable';
|
export * from './types/Nullable';
|
||||||
|
|||||||
Reference in New Issue
Block a user