diff --git a/functions/src/userUpdates/updateMapPins.ts b/functions/src/userUpdates/updateMapPins.ts index a8c85d18e5..7d769f26af 100644 --- a/functions/src/userUpdates/updateMapPins.ts +++ b/functions/src/userUpdates/updateMapPins.ts @@ -4,6 +4,7 @@ import { db } from '../Firebase/firestoreDB' import { getCreatorImage, getFirstCoverImage, + getValidTags, hasDetailsForMapPinChanged, } from './utils' @@ -32,14 +33,15 @@ export const updateMapPins = async (prevUser: IUserDB, user: IUserDB) => { coverImages, displayName, isContactableByPublic, + location, profileType, - workspaceType, userImage, - location, + workspaceType, } = user const creatorImage = getCreatorImage(userImage) const coverImage = getFirstCoverImage(coverImages) const countryCode = location?.countryCode || country || '' + const tags = user.tags ? getValidTags(user.tags) : [] const creator = { _lastActive, @@ -50,8 +52,9 @@ export const updateMapPins = async (prevUser: IUserDB, user: IUserDB) => { displayName, isContactableByPublic, profileType, - workspaceType, + tags, userImage: creatorImage, + workspaceType, } // Only one expected diff --git a/functions/src/userUpdates/utils.ts b/functions/src/userUpdates/utils.ts index 860ec9f1ea..5515ca7fbb 100644 --- a/functions/src/userUpdates/utils.ts +++ b/functions/src/userUpdates/utils.ts @@ -1,6 +1,8 @@ +import { profileTags } from 'oa-shared' + import { valuesAreDeepEqual } from '../Utils' -import type { IUserDB } from 'oa-shared/models/user' +import type { ISelectedTags, ITag, IUserDB } from 'oa-shared' export const hasDetailsChanged = ( prevUser: IUserDB, @@ -81,3 +83,14 @@ export const getCreatorImage = (userImage: IUserDB['userImage']) => { export const getFirstCoverImage = (coverImages: IUserDB['coverImages']) => { return coverImages?.[0]?.downloadUrl || null } + +// For ease, duplicated from src/utils/getValidTags.ts +export const getValidTags = (tagIds: ISelectedTags) => { + const selectedTagIds = Object.keys(tagIds).filter((id) => tagIds[id] === true) + const tags: ITag[] = selectedTagIds + .map((id) => profileTags.find(({ _id }) => id === _id)) + .filter((tag): tag is ITag => !!tag) + .filter(({ _deleted }) => _deleted !== true) + + return tags +} diff --git a/packages/components/src/CardProfile/CardDetailsSpaceProfile.tsx b/packages/components/src/CardProfile/CardDetailsSpaceProfile.tsx index 6879ce7a28..6949b5241a 100644 --- a/packages/components/src/CardProfile/CardDetailsSpaceProfile.tsx +++ b/packages/components/src/CardProfile/CardDetailsSpaceProfile.tsx @@ -96,7 +96,7 @@ export const CardDetailsSpaceProfile = ({ creator, isLink }: IProps) => { /> )} - {tags && } + {tags && } {about && ( diff --git a/packages/components/src/ProfileTagsList/ProfileTagsList.stories.tsx b/packages/components/src/ProfileTagsList/ProfileTagsList.stories.tsx index 9056c6b71d..5df447e884 100644 --- a/packages/components/src/ProfileTagsList/ProfileTagsList.stories.tsx +++ b/packages/components/src/ProfileTagsList/ProfileTagsList.stories.tsx @@ -9,6 +9,19 @@ export default { export const Default: StoryFn = () => ( ) diff --git a/packages/components/src/ProfileTagsList/ProfileTagsList.test.tsx b/packages/components/src/ProfileTagsList/ProfileTagsList.test.tsx index d048994d15..0d6b9564ff 100644 --- a/packages/components/src/ProfileTagsList/ProfileTagsList.test.tsx +++ b/packages/components/src/ProfileTagsList/ProfileTagsList.test.tsx @@ -9,11 +9,8 @@ import type { IProps } from './ProfileTagsList' describe('ProfileTagsList', () => { it('validates the component behaviour', () => { - const { getByText, queryByText } = render( - , - ) + const { getByText } = render() expect(getByText('Electronics')).toBeInTheDocument() - expect(queryByText('Machining')).toBeNull() }) }) diff --git a/packages/components/src/ProfileTagsList/ProfileTagsList.tsx b/packages/components/src/ProfileTagsList/ProfileTagsList.tsx index f32769919d..7ed4c4b1e1 100644 --- a/packages/components/src/ProfileTagsList/ProfileTagsList.tsx +++ b/packages/components/src/ProfileTagsList/ProfileTagsList.tsx @@ -1,24 +1,14 @@ -import { profileTags } from 'oa-shared' import { Flex } from 'theme-ui' import { Category } from '../Category/Category' -import type { ISelectedTags } from 'oa-shared' +import type { ITag } from 'oa-shared' export interface IProps { - tagIds: ISelectedTags + tags: ITag[] } -export const ProfileTagsList = ({ tagIds }: IProps) => { - const selectedTagIds = Object.keys(tagIds).filter((id) => tagIds[id] === true) - const tags = selectedTagIds - .map((id) => profileTags.find(({ _id }) => id === _id)) - .filter((tag) => tag !== undefined) - - if (tags.length === 0) { - return null - } - +export const ProfileTagsList = ({ tags }: IProps) => { return ( {tags.map( diff --git a/shared/models/maps.ts b/shared/models/maps.ts index 6c8ed0bdaa..e050c1d834 100644 --- a/shared/models/maps.ts +++ b/shared/models/maps.ts @@ -1,6 +1,6 @@ import type { ILatLng } from './common' import type { IModerationStatus } from './moderation' -import type { ISelectedTags } from './tags' +import type { ITag } from './tags' import type { IUserBadges, ProfileTypeName, WorkspaceType } from './user' /** @@ -69,7 +69,7 @@ export interface IProfileCreator { displayName: string isContactableByPublic: boolean profileType: ProfileTypeName - tags?: ISelectedTags + tags?: ITag[] workspaceType?: string userImage?: string } diff --git a/src/common/ProfileTags.tsx b/src/common/ProfileTags.tsx new file mode 100644 index 0000000000..d16bd97f86 --- /dev/null +++ b/src/common/ProfileTags.tsx @@ -0,0 +1,18 @@ +import { ProfileTagsList } from 'oa-components' +import { getValidTags } from 'src/utils/getValidTags' + +import type { ISelectedTags } from 'oa-shared' + +interface IProps { + tagIds: ISelectedTags +} + +export const ProfileTags = ({ tagIds }: IProps) => { + const tags = getValidTags(tagIds) + + if (tags.length === 0) { + return null + } + + return +} diff --git a/src/pages/User/content/SpaceProfile.tsx b/src/pages/User/content/SpaceProfile.tsx index 8bb306a212..8a2c163adf 100644 --- a/src/pages/User/content/SpaceProfile.tsx +++ b/src/pages/User/content/SpaceProfile.tsx @@ -1,7 +1,6 @@ import { ImageGallery, MemberBadge, - ProfileTagsList, Tab, TabPanel, Tabs, @@ -19,6 +18,7 @@ import PPIcon from 'src/assets/images/plastic-types/pp.svg' import PSIcon from 'src/assets/images/plastic-types/ps.svg' import PVCIcon from 'src/assets/images/plastic-types/pvc.svg' import { AuthWrapper } from 'src/common/AuthWrapper' +import { ProfileTags } from 'src/common/ProfileTags' import { isPreciousPlastic } from 'src/config/config' import { cdnImageUrl } from 'src/utils/cdnImageUrl' import { formatImagesForGallery } from 'src/utils/formatImageListForGallery' @@ -247,7 +247,7 @@ export const SpaceProfile = ({ user, docs }: IProps) => { width: ['100%', '100%', '80%'], }} > - {tags && } + {tags && } {about && {about}} {profileType === ProfileTypeList.COLLECTION_POINT && diff --git a/src/pages/UserSettings/SettingsPageUserProfile.tsx b/src/pages/UserSettings/SettingsPageUserProfile.tsx index 6ae83de95a..26e960d0ea 100644 --- a/src/pages/UserSettings/SettingsPageUserProfile.tsx +++ b/src/pages/UserSettings/SettingsPageUserProfile.tsx @@ -110,7 +110,7 @@ export const SettingsPageUserProfile = () => { user.isContactableByPublic || DEFAULT_PUBLIC_CONTACT_PREFERENCE, userImage: user.userImage || null, coverImages, - tags: user.tags || [], + tags: user.tags || {}, } const formId = 'userProfileForm' diff --git a/src/pages/UserSettings/content/sections/ProfileTags.section.tsx b/src/pages/UserSettings/content/sections/ProfileTags.section.tsx index 76350a67d0..e47e1183c7 100644 --- a/src/pages/UserSettings/content/sections/ProfileTags.section.tsx +++ b/src/pages/UserSettings/content/sections/ProfileTags.section.tsx @@ -2,6 +2,7 @@ import { Field } from 'react-final-form' import TagsSelect from 'src/common/Tags/TagsSelect' import { fields } from 'src/pages/UserSettings/labels' import { userService } from 'src/services/user.service' +import { COMPARISONS } from 'src/utils/comparisons' import { Flex, Heading, Text } from 'theme-ui' import { FlexSectionContainer } from '../elements' @@ -21,7 +22,11 @@ export const ProfileTags = () => { > {title} {description} - + ) diff --git a/src/stores/Maps/maps.store.ts b/src/stores/Maps/maps.store.ts index 36ffcb8771..626dfa88b9 100644 --- a/src/stores/Maps/maps.store.ts +++ b/src/stores/Maps/maps.store.ts @@ -2,6 +2,7 @@ import { action, makeObservable, observable } from 'mobx' import { IModerationStatus, ProfileTypeList } from 'oa-shared' import { logger } from 'src/logger' import { DEFAULT_PUBLIC_CONTACT_PREFERENCE } from 'src/pages/UserSettings/constants' +import { getValidTags } from 'src/utils/getValidTags' import { hasAdminRights, isAllowedToPin, @@ -180,7 +181,6 @@ export class MapsStore extends ModuleStore { location, profileType, userImage, - tags, verified, workspaceType, } = user @@ -193,6 +193,7 @@ export class MapsStore extends ModuleStore { const isMember = type === ProfileTypeList.MEMBER const subType = !isMember && workspaceType + const tags = user.tags && getValidTags(user.tags) // Member pins do not require moderation. if (isMember) { diff --git a/src/utils/getValidTags.test.ts b/src/utils/getValidTags.test.ts new file mode 100644 index 0000000000..b6fa594283 --- /dev/null +++ b/src/utils/getValidTags.test.ts @@ -0,0 +1,16 @@ +import { describe, expect, it } from 'vitest' + +import { getValidTags } from './getValidTags' + +describe('getValidTags', () => { + it('returns only true valid tags', () => { + const tagIds = { + uCzWZbz3aVKyx2keoqRi: true, // A valid tag + J3LF7fMsDfniYT2ZX3rf: false, // A valid tag + 'not-a-tag': false, + } + const tags = getValidTags(tagIds) + expect(tags.length).toEqual(1) + expect(tags[0]._id).toEqual('uCzWZbz3aVKyx2keoqRi') + }) +}) diff --git a/src/utils/getValidTags.ts b/src/utils/getValidTags.ts new file mode 100644 index 0000000000..9c89149084 --- /dev/null +++ b/src/utils/getValidTags.ts @@ -0,0 +1,15 @@ +import { profileTags } from 'oa-shared' + +import type { ISelectedTags, ITag } from 'oa-shared' + +// For ease, duplicated from functions/src/uperUpdates/utils.ts +export const getValidTags = (tagIds: ISelectedTags): ITag[] => { + const selectedTagIds = Object.keys(tagIds).filter((id) => tagIds[id] === true) + + const tags: ITag[] = selectedTagIds + .map((id) => profileTags.find(({ _id }) => id === _id)) + .filter((tag): tag is ITag => !!tag) + .filter(({ _deleted }) => _deleted !== true) + + return tags +}