diff --git a/packages/shared/src/components/icons/Bluesky/index.tsx b/packages/shared/src/components/icons/Bluesky/index.tsx new file mode 100644 index 0000000000..5e5813d01c --- /dev/null +++ b/packages/shared/src/components/icons/Bluesky/index.tsx @@ -0,0 +1,9 @@ +import type { ReactElement } from 'react'; +import React from 'react'; +import type { IconProps } from '../../Icon'; +import Icon from '../../Icon'; +import MonoIcon from './mono.svg'; + +export const BlueskyIcon = (props: IconProps): ReactElement => ( + +); diff --git a/packages/shared/src/components/icons/Bluesky/mono.svg b/packages/shared/src/components/icons/Bluesky/mono.svg new file mode 100644 index 0000000000..16b74284f1 --- /dev/null +++ b/packages/shared/src/components/icons/Bluesky/mono.svg @@ -0,0 +1,12 @@ + + + \ No newline at end of file diff --git a/packages/shared/src/components/icons/index.ts b/packages/shared/src/components/icons/index.ts index e8ad5b43ed..18357ab6c2 100644 --- a/packages/shared/src/components/icons/index.ts +++ b/packages/shared/src/components/icons/index.ts @@ -8,6 +8,7 @@ export * from './Arrow'; export * from './At'; export * from './Bell'; export * from './Block'; +export * from './Bluesky'; export * from './Bookmark'; export * from './BringForward'; export * from './Calendar'; diff --git a/packages/shared/src/components/profile/ProfileWidgets.spec.tsx b/packages/shared/src/components/profile/ProfileWidgets.spec.tsx index d2d5b6bbab..41151c87cd 100644 --- a/packages/shared/src/components/profile/ProfileWidgets.spec.tsx +++ b/packages/shared/src/components/profile/ProfileWidgets.spec.tsx @@ -44,6 +44,7 @@ const defaultLoggedUser: LoggedUser = { youtube: 'idoshamun', linkedin: 'idoshamun', mastodon: 'https://mastodon.social/@idoshamun', + bluesky: 'https://bsky.app/profile/dailydotdev.bsky.social', }; const defaultProfile: PublicProfile = { @@ -69,6 +70,7 @@ const defaultProfile: PublicProfile = { youtube: 'dailydotdev', linkedin: 'dailydotdev', mastodon: 'https://mastodon.social/@dailydotdev', + bluesky: 'https://bsky.app/profile/dailydotdev.bsky.social', }; const defaultMemberships: Connection = { @@ -289,6 +291,16 @@ it('should show mastodon link', () => { expect(el).toHaveTextContent('mastodon.social/@dailydotdev'); }); +it('should show bluesky link', () => { + renderComponent(); + const el = screen.getByTestId('bluesky'); + expect(el).toHaveAttribute( + 'href', + 'https://bsky.app/profile/dailydotdev.bsky.social', + ); + expect(el).toHaveTextContent('bsky.app/profile/dailydotdev.bsky.social'); +}); + it('should show portfolio link', async () => { renderComponent(); const el = screen.getByTestId('portfolio'); diff --git a/packages/shared/src/components/profile/SocialChips.tsx b/packages/shared/src/components/profile/SocialChips.tsx index 74c1ee8b95..eb32002a04 100644 --- a/packages/shared/src/components/profile/SocialChips.tsx +++ b/packages/shared/src/components/profile/SocialChips.tsx @@ -10,6 +10,7 @@ import { RedditIcon, RoadmapIcon, MastodonIcon, + BlueskyIcon, ThreadsIcon, CodePenIcon, } from '../icons'; @@ -32,6 +33,7 @@ export interface SocialChipsProps { youtube?: string; linkedin?: string; mastodon?: string; + bluesky?: string; }; } @@ -89,6 +91,11 @@ const handlers: Record< href: (x) => x, label: (x) => withoutProtocol(x), }, + bluesky: { + icon: , + href: (x) => x, + label: (x) => `https://bsky.app/profile/${x}`, + }, threads: { icon: , href: (x) => `https://threads.net/@${x}`, @@ -111,6 +118,7 @@ const order: (keyof SocialChipsProps['links'])[] = [ 'roadmap', 'codepen', 'mastodon', + 'bluesky', 'threads', ]; diff --git a/packages/shared/src/graphql/users.ts b/packages/shared/src/graphql/users.ts index c2b50e448d..0cdf1186dc 100644 --- a/packages/shared/src/graphql/users.ts +++ b/packages/shared/src/graphql/users.ts @@ -45,6 +45,7 @@ export const USER_BY_ID_STATIC_FIELDS_QUERY = ` youtube linkedin mastodon + bluesky timezone portfolio reputation @@ -304,6 +305,7 @@ export const UPDATE_USER_PROFILE_MUTATION = gql` youtube linkedin mastodon + bluesky createdAt infoConfirmed timezone diff --git a/packages/shared/src/hooks/useProfileForm.ts b/packages/shared/src/hooks/useProfileForm.ts index 48aac6b05c..0a46a89d54 100644 --- a/packages/shared/src/hooks/useProfileForm.ts +++ b/packages/shared/src/hooks/useProfileForm.ts @@ -27,6 +27,7 @@ export interface ProfileFormHint { youtube?: string; linkedin?: string; mastodon?: string; + bluesky?: string; } export interface UpdateProfileParameters extends Partial { @@ -64,6 +65,7 @@ type Handles = Pick< | 'youtube' | 'linkedin' | 'mastodon' + | 'bluesky' >; const socials: Array = [ 'github', @@ -77,6 +79,7 @@ const socials: Array = [ 'youtube', 'linkedin', 'mastodon', + 'bluesky', ]; export const onValidateHandles = ( diff --git a/packages/shared/src/lib/user.ts b/packages/shared/src/lib/user.ts index cfaff3fbce..100d9dc417 100644 --- a/packages/shared/src/lib/user.ts +++ b/packages/shared/src/lib/user.ts @@ -39,6 +39,7 @@ export interface PublicProfile { youtube?: string; linkedin?: string; mastodon?: string; + bluesky?: string; bio?: string; createdAt: string; premium: boolean; @@ -81,6 +82,7 @@ export interface UserProfile { youtube?: string; linkedin?: string; mastodon?: string; + bluesky?: string; portfolio?: string; bio?: string; acceptedMarketing?: boolean; diff --git a/packages/webapp/__tests__/AccountProfilePage.tsx b/packages/webapp/__tests__/AccountProfilePage.tsx index dd5eb4c111..f8b3e2948b 100644 --- a/packages/webapp/__tests__/AccountProfilePage.tsx +++ b/packages/webapp/__tests__/AccountProfilePage.tsx @@ -36,6 +36,7 @@ const defaultLoggedUser: LoggedUser = { youtube: 'dailydotdev', linkedin: 'dailydotdev', mastodon: 'https://mastodon.social/@dailydotdev', + bluesky: 'https://bsky.app/profile/dailydotdev.bsky.social', }; const updateUser = jest.fn(); @@ -122,4 +123,7 @@ it('should show profile social links', () => { const mastodon = screen.getByPlaceholderText('Mastodon'); expect(mastodon).toBeInTheDocument(); expect(mastodon).toHaveValue(defaultLoggedUser.mastodon); + const bluesky = screen.getByPlaceholderText('Bluesky'); + expect(bluesky).toBeInTheDocument(); + expect(bluesky).toHaveValue(defaultLoggedUser.bluesky); }); diff --git a/packages/webapp/components/layouts/AccountLayout/Profile/index.tsx b/packages/webapp/components/layouts/AccountLayout/Profile/index.tsx index fe7b07c03c..9f4d1f49dd 100644 --- a/packages/webapp/components/layouts/AccountLayout/Profile/index.tsx +++ b/packages/webapp/components/layouts/AccountLayout/Profile/index.tsx @@ -13,6 +13,7 @@ import { LinkedInIcon, LinkIcon, MastodonIcon, + BlueskyIcon, RedditIcon, RoadmapIcon, StackOverflowIcon, @@ -88,6 +89,7 @@ const ProfileIndex = ({ youtube: values.youtube, linkedin: values.linkedin, mastodon: values.mastodon ? withHttps(values.mastodon) : null, + bluesky: values.bluesky, experienceLevel: values.experienceLevel, onUpdateSuccess: () => router.push(`/${values.username}`).then(() => { @@ -285,7 +287,6 @@ const ProfileIndex = ({ hint={hint.portfolio} valid={!hint.portfolio} name="portfolio" - type="url" value={user?.portfolio} placeholder="example.com" /> @@ -356,10 +357,19 @@ const ProfileIndex = ({ hint={hint.mastodon} valid={!hint.mastodon} name="mastodon" - type="url" value={user?.mastodon} placeholder="mastodon.social/@username" /> + } + label="Bluesky" + inputId="bluesky" + hint={hint.bluesky} + valid={!hint.bluesky} + name="bluesky" + value={user?.bluesky} + placeholder="bsky.app/profile/username" + /> } label="Threads"