diff --git a/packages/components/src/ArticleCallToAction/ArticleCallToAction.stories.tsx b/packages/components/src/ArticleCallToAction/ArticleCallToAction.stories.tsx
index 6ab0c6e735..e8b3b32290 100644
--- a/packages/components/src/ArticleCallToAction/ArticleCallToAction.stories.tsx
+++ b/packages/components/src/ArticleCallToAction/ArticleCallToAction.stories.tsx
@@ -52,6 +52,7 @@ export const ArticleCallToActionSingleContributor: StoryFn<
countryCode: faker.address.countryCode(),
userName: faker.internet.userName(),
isVerified: faker.datatype.boolean(),
+ isSupporter: faker.datatype.boolean(),
},
]}
>
@@ -63,6 +64,7 @@ const makeFakeUser = () => ({
countryCode: faker.address.countryCode(),
userName: faker.internet.userName(),
isVerified: faker.datatype.boolean(),
+ isSupporter: faker.datatype.boolean(),
})
export const ArticleCallToActionMultipleContributors: StoryFn<
diff --git a/packages/components/src/ArticleCallToAction/ArticleCallToAction.tsx b/packages/components/src/ArticleCallToAction/ArticleCallToAction.tsx
index ffac32f9a3..c61371d18b 100644
--- a/packages/components/src/ArticleCallToAction/ArticleCallToAction.tsx
+++ b/packages/components/src/ArticleCallToAction/ArticleCallToAction.tsx
@@ -5,14 +5,16 @@ import { Username } from '../Username/Username'
import type { User } from '../types/common'
-export interface Props {
- author: User & { isVerified: boolean }
+export interface IProps {
+ author: User
children: React.ReactNode
- contributors?: (User & { isVerified: boolean })[]
+ contributors?: User[]
}
-export const ArticleCallToAction = (props: Props) => {
+export const ArticleCallToAction = (props: IProps) => {
+ const { author, children, contributors } = props
const theme = useTheme() as any
+
return (
{
>
Made by
-
+
- {props.contributors && props?.contributors.length ? (
+ {contributors && contributors.length ? (
With contributions from:{' '}
- {props.contributors.map((contributor, key) => (
+ {contributors.map((contributor, key) => (
{
},
}}
>
- {props.children}
+ {children}
)
diff --git a/packages/components/src/CommentItem/CommentItem.tsx b/packages/components/src/CommentItem/CommentItem.tsx
index 50f24e794e..52ab347f43 100644
--- a/packages/components/src/CommentItem/CommentItem.tsx
+++ b/packages/components/src/CommentItem/CommentItem.tsx
@@ -40,6 +40,7 @@ export const CommentItem = (props: CommentItemProps) => {
creatorName,
creatorCountry,
isUserVerified,
+ isUserSupporter,
isEditable,
_created,
_edited,
@@ -86,8 +87,9 @@ export const CommentItem = (props: CommentItemProps) => {
user={{
userName: creatorName,
countryCode: creatorCountry,
+ isVerified: !!isUserVerified,
+ isSupporter: !!isUserSupporter,
}}
- isVerified={!!isUserVerified}
/>
{_edited && (
(Edited)
diff --git a/packages/components/src/CommentItem/types.ts b/packages/components/src/CommentItem/types.ts
index 5c6dcc61eb..a4fb702bce 100644
--- a/packages/components/src/CommentItem/types.ts
+++ b/packages/components/src/CommentItem/types.ts
@@ -1,6 +1,7 @@
export interface IComment {
text: string
isUserVerified?: boolean
+ isUserSupporter?: boolean
isEditable: boolean
creatorCountry?: string | null
creatorName: string
diff --git a/packages/components/src/MapMemberCard/MapMemberCard.stories.tsx b/packages/components/src/MapMemberCard/MapMemberCard.stories.tsx
index 0771cf8dce..fcff4e5b3d 100644
--- a/packages/components/src/MapMemberCard/MapMemberCard.stories.tsx
+++ b/packages/components/src/MapMemberCard/MapMemberCard.stories.tsx
@@ -14,9 +14,10 @@ export const Default = {
imageUrl: 'https://placekitten.com/450/450',
description: `${faker.lorem.sentence()}`,
user: {
- username: faker.internet.userName(),
+ countryCode: faker.address.countryCode('alpha-2'),
+ userName: faker.internet.userName(),
+ isSupporter: faker.datatype.boolean(),
isVerified: faker.datatype.boolean(),
- country: faker.address.countryCode('alpha-2'),
},
heading: `${faker.lorem.word()}`,
isEditable: false,
@@ -30,9 +31,10 @@ export const LoadingState = {
imageUrl: 'https://placekitten.com/450/450',
description: `${faker.lorem.sentence()}`,
user: {
- username: faker.internet.userName(),
+ countryCode: faker.address.countryCode('alpha-2'),
+ userName: faker.internet.userName(),
+ isSupporter: faker.datatype.boolean(),
isVerified: faker.datatype.boolean(),
- country: faker.address.countryCode('alpha-2'),
},
heading: `${faker.lorem.word()}`,
isEditable: false,
@@ -45,9 +47,10 @@ export const ModerationComments = {
description: `${faker.lorem.sentence()}`,
comments: `${faker.lorem.sentence()}`,
user: {
- username: faker.internet.userName(),
+ countryCode: faker.address.countryCode('alpha-2'),
+ userName: faker.internet.userName(),
+ isSupporter: faker.datatype.boolean(),
isVerified: faker.datatype.boolean(),
- country: faker.address.countryCode('alpha-2'),
},
heading: `${faker.lorem.word()}`,
isEditable: false,
diff --git a/packages/components/src/MapMemberCard/MapMemberCard.tsx b/packages/components/src/MapMemberCard/MapMemberCard.tsx
index 4a659c6ba5..846ecc1055 100644
--- a/packages/components/src/MapMemberCard/MapMemberCard.tsx
+++ b/packages/components/src/MapMemberCard/MapMemberCard.tsx
@@ -1,23 +1,20 @@
+import { keyframes } from '@emotion/react'
import { Alert, AspectRatio, Box, Card, Image, Text } from 'theme-ui'
import { Username } from '../Username/Username'
+import type { User } from '../types/common'
+
export interface Props {
loading?: boolean
imageUrl: string
description: string
comments: string | null
- user: {
- isVerified: boolean
- username: string
- country: string | null
- }
+ user: User
heading: string
isEditable: boolean
}
-import { keyframes } from '@emotion/react'
-
const wave = keyframes`
from {
background: lightgrey;
@@ -29,6 +26,7 @@ const wave = keyframes`
export const MapMemberCard = (props: Props) => {
const { imageUrl, description, user, heading, comments } = props
+
return (
@@ -62,13 +60,7 @@ export const MapMemberCard = (props: Props) => {
{heading}
-
+
{
)}
- {hasLocation ? (
-
-
-
- {props.country || 'View on Map'}
-
-
- ) : null}
-
{props?.isSupporter ? (
@@ -74,6 +61,19 @@ export const UserStatistics = (props: UserStatisticsProps) => {
) : null}
+ {hasLocation ? (
+
+
+
+ {props.country || 'View on Map'}
+
+
+ ) : null}
+
{props.usefulCount ? (
diff --git a/packages/components/src/Username/Username.stories.tsx b/packages/components/src/Username/Username.stories.tsx
index 3e1778c637..d5614d883c 100644
--- a/packages/components/src/Username/Username.stories.tsx
+++ b/packages/components/src/Username/Username.stories.tsx
@@ -12,8 +12,9 @@ export const Verified = {
user: {
userName: 'a-username',
countryCode: 'pt',
+ isSupporter: false,
+ isVerified: true,
},
- isVerified: true,
},
}
@@ -22,8 +23,31 @@ export const Unverified = {
user: {
countryCode: 'pt',
userName: 'a-username',
+ isVerified: false,
+ isSupporter: false,
+ },
+ },
+}
+
+export const VerifiedSupporter = {
+ args: {
+ user: {
+ countryCode: 'pt',
+ userName: 'a-username',
+ isVerified: true,
+ isSupporter: true,
+ },
+ },
+}
+
+export const UnverifiedSupporter = {
+ args: {
+ user: {
+ countryCode: 'pt',
+ userName: 'a-username',
+ isVerified: false,
+ isSupporter: true,
},
- isVerified: false,
},
}
@@ -32,7 +56,6 @@ export const WithoutFlag = {
user: {
userName: 'a-username',
},
- isVerified: false,
},
}
@@ -42,7 +65,6 @@ export const InvalidCountryCode = {
userName: 'a-username',
countryCode: 'zz',
},
- isVerified: false,
},
}
@@ -54,6 +76,5 @@ export const InlineStyles = {
sx: {
outline: '10px solid red',
},
- isVerified: false,
},
}
diff --git a/packages/components/src/Username/Username.test.tsx b/packages/components/src/Username/Username.test.tsx
index e42fa7fede..3bb84101ab 100644
--- a/packages/components/src/Username/Username.test.tsx
+++ b/packages/components/src/Username/Username.test.tsx
@@ -1,6 +1,12 @@
import { render } from '../tests/utils'
import { Username } from './Username'
-import { InvalidCountryCode, WithoutFlag } from './Username.stories'
+import {
+ InvalidCountryCode,
+ UnverifiedSupporter,
+ Verified,
+ VerifiedSupporter,
+ WithoutFlag,
+} from './Username.stories'
describe('Username', () => {
it('shows an unknown flag for empty value', () => {
@@ -14,4 +20,19 @@ describe('Username', () => {
expect(getByTestId('Username: unknown flag')).toBeInTheDocument()
})
+
+ it('shows a verified badge when the user is verified', () => {
+ const { getByTestId } = render()
+ expect(getByTestId('Username: verified badge')).toBeInTheDocument()
+ })
+
+ it('shows a supporter badge when the user is a supporter and also unverified', () => {
+ const { getByTestId } = render()
+ expect(getByTestId('Username: supporter badge')).toBeInTheDocument()
+ })
+
+ it('shows a verified badge when the user is verified and also a supporter', () => {
+ const { getByTestId } = render()
+ expect(getByTestId('Username: verified badge')).toBeInTheDocument()
+ })
})
diff --git a/packages/components/src/Username/Username.tsx b/packages/components/src/Username/Username.tsx
index 870533a0de..f5da5d77ed 100644
--- a/packages/components/src/Username/Username.tsx
+++ b/packages/components/src/Username/Username.tsx
@@ -2,6 +2,7 @@ import { Flex, Image, Text } from 'theme-ui'
import flagUnknownSVG from '../../assets/icons/flag-unknown.svg'
import VerifiedBadgeIcon from '../../assets/icons/icon-verified-badge.svg'
+import SupporterBadgeIcon from '../../assets/icons/supporter.svg'
import { FlagIconHowTos } from '../FlagIcon/FlagIcon'
import { InternalLink } from '../InternalLink/InternalLink'
import { twoCharacterCountryCodes } from './TwoCharacterCountryCodes'
@@ -9,8 +10,7 @@ import { twoCharacterCountryCodes } from './TwoCharacterCountryCodes'
import type { ThemeUIStyleObject } from 'theme-ui'
import type { User } from '../types/common'
-export interface Props {
- isVerified: boolean
+export interface IProps {
user: User
sx?: ThemeUIStyleObject
}
@@ -18,11 +18,13 @@ export interface Props {
const isValidCountryCode = (str: string) =>
str && twoCharacterCountryCodes.has(str.toUpperCase())
-export const Username = (props: Props) => {
+export const Username = ({ user, sx }: IProps) => {
+ const { countryCode, userName, isSupporter, isVerified } = user
+
return (
{
background: 'softblue',
color: 'bluetag',
},
- ...(props.sx || {}),
+ ...(sx || {}),
}}
>
{
}}
>
- {props.user.countryCode &&
- isValidCountryCode(props.user.countryCode) ? (
+ {countryCode && isValidCountryCode(countryCode) ? (
-
+
) : (
{
>
)}
- {props.user.userName}
- {props.isVerified && (
+ {userName}
+ {isVerified && (
)}
+ {isSupporter && !isVerified && (
+
+ )}
)
diff --git a/packages/components/src/types/common.ts b/packages/components/src/types/common.ts
index 2b3535bab2..fa7c2b0866 100644
--- a/packages/components/src/types/common.ts
+++ b/packages/components/src/types/common.ts
@@ -1,4 +1,6 @@
export type User = {
- countryCode?: string | null
userName: string
+ countryCode?: string | null
+ isSupporter?: boolean
+ isVerified?: boolean
}
diff --git a/packages/components/src/utils.ts b/packages/components/src/utils.ts
index f7014c8e14..ca8314dc50 100644
--- a/packages/components/src/utils.ts
+++ b/packages/components/src/utils.ts
@@ -9,6 +9,7 @@ export const fakeComment = (commentOverloads: Partial = {}) => ({
_id: faker.database.mongodbObjectId(),
creatorName: faker.internet.userName(),
isUserVerified: faker.datatype.boolean(),
+ isUserSupporter: faker.datatype.boolean(),
text: faker.lorem.text(),
isEditable: faker.datatype.boolean(),
...commentOverloads,
diff --git a/src/models/discussion.models.tsx b/src/models/discussion.models.tsx
index d7cb8dcb8e..bfd68dc894 100644
--- a/src/models/discussion.models.tsx
+++ b/src/models/discussion.models.tsx
@@ -7,6 +7,7 @@ import type { IQuestion } from './question.models'
*/
export type IDiscussionComment = IComment & {
parentCommentId: string | null
+ isUserSupporter?: boolean
}
export type IDiscussion = {
diff --git a/src/pages/Howto/Content/Howto/Howto.tsx b/src/pages/Howto/Content/Howto/Howto.tsx
index 1b2d4e4465..a2a48c3287 100644
--- a/src/pages/Howto/Content/Howto/Howto.tsx
+++ b/src/pages/Howto/Content/Howto/Howto.tsx
@@ -110,7 +110,10 @@ export const Howto = observer(() => {
}
const hasUserVotedUseful = howtoStore.userVotedActiveHowToUseful
-
+ const isVerified = isUserVerifiedWithStore(
+ howto._createdBy,
+ aggregationsStore,
+ )
return (
<>
{
author={{
userName: howto._createdBy,
countryCode: howto.creatorCountry,
- isVerified: isUserVerifiedWithStore(
- howto._createdBy,
- aggregationsStore,
- ),
+ isVerified,
}}
>
diff --git a/src/pages/Maps/Content/MapView/Popup.tsx b/src/pages/Maps/Content/MapView/Popup.tsx
index ebba760443..2af7ae5ce9 100644
--- a/src/pages/Maps/Content/MapView/Popup.tsx
+++ b/src/pages/Maps/Content/MapView/Popup.tsx
@@ -67,8 +67,8 @@ export const Popup = (props: IProps) => {
description={activePin.detail?.shortDescription}
user={{
isVerified: !!activePin.detail?.verifiedBadge,
- username: activePin.detail?.name,
- country: activePin.detail?.country,
+ userName: activePin.detail?.name,
+ countryCode: activePin.detail?.country?.toLowerCase(),
}}
heading={getHeading(activePin)}
isEditable={!!mapsStore.needsModeration(activePin)}
diff --git a/src/pages/Research/Content/ResearchDescription.tsx b/src/pages/Research/Content/ResearchDescription.tsx
index 479d5e7cb7..a43d4d6c5c 100644
--- a/src/pages/Research/Content/ResearchDescription.tsx
+++ b/src/pages/Research/Content/ResearchDescription.tsx
@@ -257,11 +257,7 @@ const ResearchDescription = ({
{props.contributors.map((contributor, key) => (
-
+
))}
) : null}
diff --git a/src/pages/Research/Content/ResearchListItem.tsx b/src/pages/Research/Content/ResearchListItem.tsx
index 56a52b99f6..ffbd579cb7 100644
--- a/src/pages/Research/Content/ResearchListItem.tsx
+++ b/src/pages/Research/Content/ResearchListItem.tsx
@@ -46,6 +46,8 @@ const ResearchListItem = ({ item }: IProps) => {
fontSize: [1, 2, 2],
}
+ const isVerified = isUserVerifiedWithStore(item._createdBy, aggregationsStore)
+
const status = item.researchStatus || ResearchStatus.IN_PROGRESS
return (
@@ -139,11 +141,8 @@ const ResearchListItem = ({ item }: IProps) => {
user={{
userName: item._createdBy,
countryCode: item.creatorCountry,
+ isVerified,
}}
- isVerified={isUserVerifiedWithStore(
- item._createdBy,
- aggregationsStore,
- )}
/>
{Boolean(collaborators.length) && (
{contributors.length > 0 ? (
-
+
) : null}
diff --git a/src/pages/User/content/MemberProfile.tsx b/src/pages/User/content/MemberProfile.tsx
index 0371f4625e..abb6833b0f 100644
--- a/src/pages/User/content/MemberProfile.tsx
+++ b/src/pages/User/content/MemberProfile.tsx
@@ -3,6 +3,7 @@ import { ExternalLinkLabel } from 'oa-shared'
import DefaultMemberImage from 'src/assets/images/default_member.svg'
import { AuthWrapper } from 'src/common/AuthWrapper'
import { useMemberStatistics } from 'src/common/hooks/useMemberStatistics'
+import { getUserCountry } from 'src/utils/getUserCountry'
import { Box, Card, Flex, Heading, Image, Paragraph } from 'theme-ui'
import UserContactAndLinks from './UserContactAndLinks'
@@ -29,9 +30,6 @@ export const MemberProfile = ({ user, docs }: IProps) => {
const stats = useMemberStatistics(user.userName)
- const userCountryCode =
- user.location?.countryCode || user.country?.toLowerCase() || undefined
-
const memberPictureSource =
user.coverImages && user.coverImages[0]
? (user.coverImages[0] as IUploadedFileMeta).downloadUrl
@@ -122,9 +120,9 @@ export const MemberProfile = ({ user, docs }: IProps) => {
diff --git a/src/pages/User/content/SpaceProfile.tsx b/src/pages/User/content/SpaceProfile.tsx
index 97429a36d9..cc74b56360 100644
--- a/src/pages/User/content/SpaceProfile.tsx
+++ b/src/pages/User/content/SpaceProfile.tsx
@@ -22,6 +22,7 @@ import { isPreciousPlastic } from 'src/config/config'
import { ProfileType } from 'src/modules/profile/types'
import { UserContactForm } from 'src/pages/User/contact'
import { formatImagesForGallery } from 'src/utils/formatImageListForGallery'
+import { getUserCountry } from 'src/utils/getUserCountry'
import {
AspectRatio,
Box,
@@ -161,16 +162,8 @@ const getCoverImages = (user: IUserPP) => {
}
export const SpaceProfile = ({ user, docs }: IProps) => {
- const {
- about,
- country,
- displayName,
- impact,
- links,
- location,
- profileType,
- userName,
- } = user
+ const { about, displayName, impact, links, location, profileType, userName } =
+ user
const coverImage = getCoverImages(user)
const stats = useMemberStatistics(user.userName)
@@ -182,9 +175,6 @@ export const SpaceProfile = ({ user, docs }: IProps) => {
),
)
- const userCountryCode =
- location?.countryCode || country?.toLowerCase() || undefined
-
return (
{
@@ -28,8 +30,8 @@ export const UserNameTag = ({
user={{
userName: userName,
countryCode: countryCode,
+ isVerified,
}}
- isVerified={isUserVerifiedWithStore(userName, aggregationsStore)}
/>