feat: move patreon to profile settings

This commit is contained in:
Ben Furber
2024-07-24 15:13:58 +01:00
committed by benfurber
parent feffc0ebca
commit de444bca0e
8 changed files with 82 additions and 137 deletions

View File

@@ -0,0 +1,4 @@
<svg width="40" height="43" viewBox="0 0 40 43" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M31.6764 30.6581L33.9381 29.3784L36.3686 29.3275L38.9204 32.0306L38.7281 35.9953L35.9078 39.721L33.4294 42.2894L28.3488 40.8113L24.3834 38.5909L22.8479 35.5475L24.1185 32.34L27.0444 31.2151L29.7025 32.1216L30.3762 32.6118L31.0884 31.5063L31.6764 30.6581Z" fill="#D61F30"/>
<path d="M39.1742 13.1175C39.1668 7.7547 34.9901 3.35948 30.0897 1.77359C24.0044 -0.195741 15.9785 0.0897038 10.1677 2.83137C3.12497 6.15481 0.912581 13.4348 0.830137 20.6954C0.76247 26.6648 1.35825 42.3872 10.2265 42.4992C16.8158 42.5828 17.797 34.0922 20.8459 30.003C23.0151 27.0938 25.8081 26.272 29.2462 25.4211C35.1554 23.9585 39.1828 19.295 39.1742 13.1175Z" fill="black"/>
</svg>

After

Width:  |  Height:  |  Size: 765 B

View File

@@ -102,6 +102,7 @@ export const glyphs: IGlyphs = {
menu: <MdMenu />,
'more-vert': <MdMoreVert />,
notifications: <MdNotifications />,
patreon: iconMap.patreon,
pdf: <GoFilePdf />,
plastic: iconMap.plastic,
profile: iconMap.profile,

View File

@@ -24,6 +24,7 @@ import websiteSVG from '../../assets/icons/icon-website.svg'
import impactSVG from '../../assets/icons/impact.svg'
import machineSVG from '../../assets/icons/machine.svg'
import mapSVG from '../../assets/icons/map.svg'
import patreonSVG from '../../assets/icons/patreon.svg'
import plasticSVG from '../../assets/icons/plastic.svg'
import profileSVG from '../../assets/icons/profile.svg'
import revenueSVG from '../../assets/icons/revenue.svg'
@@ -63,6 +64,7 @@ export const iconMap = {
loading: <ImageIcon src={loadingSVG} data-cy="icon-loading" />,
machine: <ImageIcon src={machineSVG} />,
map: <ImageIcon src={mapSVG} />,
patreon: <ImageIcon src={patreonSVG} />,
plastic: <ImageIcon src={plasticSVG} />,
profile: <ImageIcon src={profileSVG} />,
revenue: <ImageIcon src={revenueSVG} />,

View File

@@ -44,6 +44,7 @@ export type availableGlyphs =
| 'menu'
| 'more-vert'
| 'notifications'
| 'patreon'
| 'pdf'
| 'plastic'
| 'profile'

View File

@@ -1,16 +1,20 @@
import React from 'react'
import { observer } from 'mobx-react'
import { ExternalLink } from 'oa-components'
import { useCommonStores } from 'src/common/hooks/useCommonStores'
import { DISCORD_INVITE_URL } from 'src/constants'
import { fields, headings } from 'src/pages/UserSettings/labels'
import { Flex, Heading, Text } from 'theme-ui'
import { ChangeEmailForm } from './ChangeEmail.form'
import { ChangePasswordForm } from './ChangePassword.form'
import { PatreonIntegration } from './PatreonIntegration'
export const AccountSettingsSection = observer(() => {
const { description, title } = fields.deleteAccount
const { userStore } = useCommonStores().stores
return (
<Flex
sx={{
@@ -25,8 +29,11 @@ export const AccountSettingsSection = observer(() => {
Here you can manage the core settings of your account.
</Text>
</Flex>
<PatreonIntegration user={userStore.activeUser} />
<ChangeEmailForm />
<ChangePasswordForm />
<Text variant="body">
{title}
<ExternalLink

View File

@@ -6,7 +6,6 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'
import {
CONNECT_BUTTON_TEXT,
HEADING,
ONE_ARMY_PATREON_URL,
PatreonIntegration,
REMOVE_BUTTON_TEXT,
SUCCESS_MESSAGE,
@@ -89,23 +88,7 @@ describe('PatreonIntegration', () => {
it('renders correctly', () => {
expect(screen.getByText(HEADING)).toBeInTheDocument()
})
it('displays instructions on how to connect and connect button', () => {
expect(screen.getByText(CONNECT_BUTTON_TEXT)).toBeInTheDocument()
expect(
screen.getByText((t) =>
t.includes('Connect your Patreon account by clicking below'),
),
).toBeInTheDocument()
})
it('links to one army patreon page', () => {
expect(
screen.getByRole(
(t, e) => e?.getAttribute('href') === ONE_ARMY_PATREON_URL,
),
).toBeInTheDocument()
})
})
@@ -129,7 +112,6 @@ describe('PatreonIntegration', () => {
expect(mockRemovePatreonConnection).toHaveBeenCalledWith(
mockUser.userName,
)
expect(screen.getByText(CONNECT_BUTTON_TEXT)).toBeInTheDocument()
})
})
@@ -150,15 +132,5 @@ describe('PatreonIntegration', () => {
screen.queryByText(WRONG_PATREON_TIER_TITLE),
).not.toBeInTheDocument()
})
it('displays become a supporter message', () => {
expect(
screen.getByText((t) =>
t.includes(
'It looks like you are not an active supporter of this project.',
),
),
).toBeInTheDocument()
})
})
})

View File

@@ -1,46 +1,26 @@
import * as React from 'react'
import { Button } from 'oa-components'
import { Button, Icon } from 'oa-components'
import { useCommonStores } from 'src/common/hooks/useCommonStores'
import { PATREON_CLIENT_ID } from 'src/config/config'
import { Box, Flex, Heading, Image, Text } from 'theme-ui'
import { Flex, Heading, Image, Text } from 'theme-ui'
import { FlexSectionContainer } from './elements'
import type { IUserPP } from 'src/models'
export const HEADING = '❤️ Become a Supporter'
const SUBHEADING =
'Support us on Patreon to get a badge here on the platform and special insights and voting rights on decisions.'
const BETA_DISCLAIMER =
'This feature is still in beta and we will continue to roll out more features for supporters.'
export const HEADING = 'Patreon'
export const SUCCESS_MESSAGE = 'Successfully linked Patreon account!'
export const SUPPORTER_MESSAGE =
'Thanks for supporting us! :) Update your data if you changed your Patreon tiers or remove the connection below.'
export const CONNECT_BUTTON_TEXT = 'Connect To Patreon'
export const UPDATE_BUTTON_TEXT = 'Update Patreon Data'
export const REMOVE_BUTTON_TEXT = 'Remove Connection'
export const CONNECT_BUTTON_TEXT = 'Connect'
export const UPDATE_BUTTON_TEXT = 'Update'
export const REMOVE_BUTTON_TEXT = 'Disconnect'
export const ONE_ARMY_PATREON_URL = 'https://www.patreon.com/one_army'
export const PatreonIntegration = (props: { user: IUserPP }) => {
export const PatreonIntegration = ({ user }) => {
const { userStore } = useCommonStores().stores
const [user, setUser] = React.useState<IUserPP>(props.user)
const removePatreonConnection = () => {
if (!user) {
return
}
userStore.removePatreonConnection(user.userName)
// Perform an optimistic update to avoid waiting for database calls to return.
setUser({
...user,
badges: {
...user.badges,
supporter: false,
},
patreon: undefined,
})
}
const patreonRedirect = () => {
@@ -60,19 +40,24 @@ export const PatreonIntegration = (props: { user: IUserPP }) => {
}
return (
<FlexSectionContainer>
<Heading as="h2" variant="small">
{HEADING}
</Heading>
<Text mt={4}>{SUBHEADING}</Text>
<Text mt={4}>{BETA_DISCLAIMER}</Text>
{user.patreon ? (
<Box mt={4} mb={4}>
<Flex
style={{
alignItems: 'center',
}}
>
<Flex
sx={{
alignItems: ['flex-start', 'flex-start', 'center'],
backgroundColor: 'offwhite',
borderRadius: 3,
flexDirection: ['column', 'column', 'row'],
justifyContent: 'space-between',
padding: 4,
gap: [2, 4],
}}
>
<Icon glyph="patreon" size={45} />
<Flex sx={{ flexDirection: 'column', flex: 1, gap: [2] }}>
<Heading as="h2" variant="small">
{HEADING}
</Heading>
{user.patreon ? (
<>
<Image
src={user.patreon.attributes.thumb_url}
sx={{
@@ -83,89 +68,64 @@ export const PatreonIntegration = (props: { user: IUserPP }) => {
}}
/>
<Text>{SUCCESS_MESSAGE}</Text>
</Flex>
{user.badges?.supporter && user.patreon.membership ? (
<Flex sx={{ flexDirection: 'column' }}>
<Text mt={4}>{SUPPORTER_MESSAGE}</Text>
{user.patreon.membership.tiers.map(({ id, attributes }) => (
<Flex
key={id}
style={{
alignItems: 'center',
}}
mt={4}
>
<div
{user.badges?.supporter && user.patreon.membership && (
<Flex sx={{ flexDirection: 'column' }}>
<Text mt={4}>{SUPPORTER_MESSAGE}</Text>
{user.patreon.membership.tiers.map(({ id, attributes }) => (
<Flex
key={id}
style={{
width: '40px',
height: '40px',
overflow: 'hidden',
marginRight: '10px',
alignItems: 'center',
}}
mt={4}
>
<Image
src={attributes.image_url}
sx={{
borderRadius: '50%',
width: 'auto',
<div
style={{
width: '40px',
height: '40px',
objectFit: 'cover',
objectPosition: 'center',
overflow: 'hidden',
marginRight: '10px',
}}
/>
</div>
<Text>{attributes.title}</Text>
</Flex>
))}
</Flex>
) : (
<Flex sx={{ flexDirection: 'column' }}>
<Text mt={4}>
Thanks for connecting your account! It looks like you are not an
active supporter of this project. You can support us{' '}
<a href={ONE_ARMY_PATREON_URL} target="_blank" rel="noreferrer">
here
</a>
. If you become a supporter in the future, click the button
below to update your data.
</Text>
</Flex>
)}
</Box>
) : (
<Text mt={4} mb={4} sx={{ display: 'block', whiteSpace: 'pre-line' }}>
How it works: <br />
1.{' '}
<a href={ONE_ARMY_PATREON_URL} target="_blank" rel="noreferrer">
Support us
</a>{' '}
on Patreon <br />
2. Connect your Patreon account by clicking below <br />
3. You are now part of the special supporters club!
</Text>
)}
<Flex>
<Button
type="button"
onClick={patreonRedirect}
mb={3}
sx={{ justifyContent: 'center', mr: 3 }}
variant="outline"
>
>
<Image
src={attributes.image_url}
sx={{
borderRadius: '50%',
width: 'auto',
height: '40px',
objectFit: 'cover',
objectPosition: 'center',
}}
/>
</div>
<Text>{attributes.title}</Text>
</Flex>
))}
</Flex>
)}
</>
) : (
<Text variant="quiet">
As a supporter you get a badge on the platform, special insights and
voting rights on decisions.
</Text>
)}
</Flex>
<Flex sx={{ flexDirection: 'column', gap: 2 }}>
<Button type="button" onClick={patreonRedirect} variant="primary">
{user.patreon ? UPDATE_BUTTON_TEXT : CONNECT_BUTTON_TEXT}
</Button>
{user.patreon && (
<Button
type="button"
onClick={removePatreonConnection}
mb={3}
sx={{ justifyContent: 'center' }}
variant="outline"
>
{REMOVE_BUTTON_TEXT}
</Button>
)}
</Flex>
</FlexSectionContainer>
</Flex>
)
}

View File

@@ -20,7 +20,6 @@ import { FlexSectionContainer } from './elements'
import { EmailNotificationsSection } from './EmailNotifications.section'
import { ExpertiseSection } from './Expertise.section'
import { FocusSection } from './Focus.section'
import { PatreonIntegration } from './PatreonIntegration'
import { PublicContactSection } from './PublicContact.section'
import { SettingsFormNotifications } from './SettingsFormNotifications'
import { UserInfosSection } from './UserInfos.section'
@@ -222,7 +221,6 @@ export const UserProfile = () => {
/>
</FlexSectionContainer>
)}
<PatreonIntegration user={user} />
</form>
<Button