Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
106 changes: 106 additions & 0 deletions src/cloud/components/UpgradeButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import React, { useCallback } from 'react'
import { useSettings } from '../lib/stores/settings'
import styled from '../lib/styled'
import cc from 'classcat'
import { primaryButtonStyle } from '../lib/styled/styleFunctions'
import {
MixpanelActionTrackTypes,
MixpanelFrontEvent,
} from '../interfaces/analytics/mixpanel'
import { trackEvent } from '../api/track'

interface UpgradeBadgeProps {
origin: 'share.password' | 'share.expire' | 'revision' | 'guest' | 'limit'
variant?: 'link' | 'primary'
label?: string
tabIndex?: number
query?: object
className?: string
}

const UpgradeButton = ({
origin,
query,
label = 'Upgrade',
variant = 'primary',
tabIndex,
className,
}: UpgradeBadgeProps) => {
const { openSettingsTab } = useSettings()

const track = useCallback(async () => {
let mixpanelEvent: MixpanelFrontEvent
switch (origin) {
case 'guest':
mixpanelEvent = MixpanelActionTrackTypes.UpgradeGuest
break
case 'limit':
mixpanelEvent = MixpanelActionTrackTypes.UpgradeLimit
break
case 'revision':
mixpanelEvent = MixpanelActionTrackTypes.UpgradeRevision
break
case 'share.expire':
mixpanelEvent = MixpanelActionTrackTypes.UpgradeExpirationDate
break
case 'share.password':
mixpanelEvent = MixpanelActionTrackTypes.UpgradePassword
break
default:
return
break
}

return trackEvent(mixpanelEvent, query)
}, [origin, query])

const onClick = useCallback(
(event: React.MouseEvent) => {
event.preventDefault()
track()
openSettingsTab('teamUpgrade')
},
[openSettingsTab, track]
)

return (
<Container
className={cc([`upgrade__${variant}`, className])}
onClick={onClick}
tabIndex={tabIndex}
>
{label}
</Container>
)
}

const Container = styled.button`
all 0.3s ease-out;

&.upgrade__primary {
border-radius: 3px;
${primaryButtonStyle}
text-transform: uppercase;
padding: 0 5px;
font-size: ${({ theme }) => theme.fontSizes.xxsmall}px;
height: auto;
line-height: 26px;
height: 26px;
}

&.upgrade__link {
outline: none;
background: none;
color: inherit;
line-height: inherit;
font-size: inherit;
text-decoration: underline;
margin: 0 ${({ theme }) => theme.space.xxxsmall}px;

&:hover {
opacity: 0.8;
}
}
`

export default UpgradeButton
27 changes: 7 additions & 20 deletions src/cloud/components/atoms/AnnouncementAlert.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
import React, { useMemo } from 'react'
import { usePage } from '../../lib/stores/pageStore'
import styled from '../../lib/styled'
import { useSettings } from '../../lib/stores/settings'
import { useOnboarding } from '../../lib/stores/onboarding'
import IconMdi from './IconMdi'
import { mdiClose } from '@mdi/js'
import { useGlobalData } from '../../lib/stores/globalData'
import { PageStoreWithTeam } from '../../interfaces/pageStore'
import UpgradeButton from '../UpgradeButton'

const AnnouncementAlert = () => {
const { currentSubInfo } = usePage()
const { openSettingsTab } = useSettings()
const { currentOnboardingState, setOnboarding } = useOnboarding()
const {
globalData: { currentUser },
Expand Down Expand Up @@ -74,15 +73,7 @@ const AnnouncementAlert = () => {
<StyledAnnouncementAlert>
<p>
You are not eligible for a free trial anymore. Please
<a
href='#'
onClick={(e: any) => {
e.preventDefault()
openSettingsTab('teamUpgrade')
}}
>
upgrade
</a>{' '}
<UpgradeButton origin='limit' variant='link' label='Upgrade' />
now.
</p>
</StyledAnnouncementAlert>
Expand All @@ -99,15 +90,11 @@ const AnnouncementAlert = () => {
<StyledAnnouncementAlert>
<p>
Your number of documents exceeds the capacity of the free plan.
<a
href='#'
onClick={(e: any) => {
e.preventDefault()
openSettingsTab('teamUpgrade')
}}
>
Start your free trial
</a>{' '}
<UpgradeButton
origin='limit'
variant='link'
label='Start your free trial'
/>
now.
</p>
</StyledAnnouncementAlert>
Expand Down
42 changes: 29 additions & 13 deletions src/cloud/components/atoms/ErrorBlock.tsx
Original file line number Diff line number Diff line change
@@ -1,27 +1,38 @@
import React, { useMemo } from 'react'
import React, { useEffect, useState } from 'react'
import { AxiosError } from 'axios'
import ColoredBlock from './ColoredBlock'
import { nodeEnv } from '../../lib/consts'
import ky from 'ky'

interface ErrorAlertProps {
error: unknown
style?: React.CSSProperties
}

const ErrorBlock = ({ error, style }: ErrorAlertProps) => {
const errorMessage = useMemo(() => {
const [message, setMessage] = useState<React.ReactNode>()

useEffect(() => {
try {
const rawMessage = getErrorMessage(error)
if (nodeEnv === 'development') {
return rawMessage.split('\n').map((message, index) => {
return <li key={index}>{message}</li>
})
async function fetchData() {
const rawMessage = await getErrorMessage(error)
if (nodeEnv === 'development') {
setMessage(
rawMessage.split('\n').map((message, index) => {
return <li key={index}>{message}</li>
})
)
return
}
const lines = rawMessage.split('\n')
setMessage(<li>{lines[0]}</li>)
return
}

const [message] = rawMessage.split('\n')
return <li>{message}</li>
fetchData()
return
} catch (err) {
return <li>{String(error)}</li>
setMessage(<li>{String(error)}</li>)
return
}
}, [error])

Expand All @@ -31,15 +42,20 @@ const ErrorBlock = ({ error, style }: ErrorAlertProps) => {
className='list-unstyled'
style={{ paddingLeft: 0, listStyle: 'none' }}
>
{errorMessage}
{message}
</ul>
</ColoredBlock>
)
}

export default ErrorBlock

export function getErrorMessage(error: unknown): string {
export async function getErrorMessage(error: unknown): Promise<string> {
if (error instanceof ky.HTTPError) {
const message = await error.response.text()
return message
}

if (isAxiosError(error)) {
return error.response!.data
}
Expand Down
10 changes: 8 additions & 2 deletions src/cloud/components/molecules/Banner/SubLimitReachedBanner.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ import { usePage } from '../../../lib/stores/pageStore'
import Banner from '.'
import CustomButton from '../../atoms/buttons/CustomButton'
import styled from '../../../lib/styled'
import { freePlanDocLimit } from '../../../lib/subscription'
import { trackEvent } from '../../../api/track'
import { MixpanelActionTrackTypes } from '../../../interfaces/analytics/mixpanel'

const DocLimitReachedBanner = () => {
const { subscription } = usePage()
Expand All @@ -15,7 +18,7 @@ const DocLimitReachedBanner = () => {
<p>
<StyledLabel>
{subscription == null
? 'Your workspace exceeds the limit of your current plan. (30 created documents)'
? `Your workspace exceeds the limit of your current plan. (${freePlanDocLimit} created documents)`
: `Your workspace exceeds the limit of your current plan. (${subscription.seats} team members)`}
</StyledLabel>
<CustomButton
Expand All @@ -25,7 +28,10 @@ const DocLimitReachedBanner = () => {
padding: '0 10px',
}}
variant='danger'
onClick={() => openSettingsTab('teamUpgrade')}
onClick={() => {
trackEvent(MixpanelActionTrackTypes.UpgradeLimit)
openSettingsTab('teamUpgrade')
}}
>
Upgrade
</CustomButton>
Expand Down
42 changes: 17 additions & 25 deletions src/cloud/components/molecules/DocShare.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,12 @@ import { Spinner } from '../atoms/Spinner'
import DatePicker from 'react-datepicker'
import { isArray } from 'util'
import 'react-datepicker/dist/react-datepicker.css'
import { useSettings } from '../../lib/stores/settings'
import IconMdi from '../atoms/IconMdi'
import { boostHubBaseUrl } from '../../lib/consts'
import { SerializedTeam } from '../../interfaces/db/team'
import { getDocLinkHref } from '../atoms/Link/DocLink'
import { usingElectron, openInBrowser } from '../../lib/stores/electron'
import UpgradeButton from '../UpgradeButton'

interface DocShareProps {
currentDoc: SerializedDocWithBookmark
Expand All @@ -65,7 +65,6 @@ const DocShare = ({ currentDoc, team }: DocShareProps) => {
const [showExpireForm, setShowExpireForm] = useState(false)
const [passwordText, setPasswordText] = useState('')
const [expireDate, setExpireDate] = useState<Date | null>(null)
const { openSettingsTab } = useSettings()

useEffect(() => {
if (currentDoc.shareLink == null) {
Expand Down Expand Up @@ -455,14 +454,14 @@ const DocShare = ({ currentDoc, team }: DocShareProps) => {
className='share__row__label'
>
<span>Password Protect</span>
{subscription == null && (
<button
{(subscription == null ||
subscription.plan === 'standard') && (
<UpgradeButton
tabIndex={-1}
origin='share.password'
className='upgrade__badge'
onClick={() => openSettingsTab('teamUpgrade')}
>
upgrade
</button>
query={{ teamId: team.id, docId: currentDoc.id }}
/>
)}
</Flexbox>
<div className='share__row__switch'>
Expand Down Expand Up @@ -508,14 +507,14 @@ const DocShare = ({ currentDoc, team }: DocShareProps) => {
className='share__row__label'
>
<span>Expiration Date</span>
{subscription == null && (
<button
{(subscription == null ||
subscription.plan === 'standard') && (
<UpgradeButton
tabIndex={-1}
origin='share.expire'
className='upgrade__badge'
onClick={() => openSettingsTab('teamUpgrade')}
>
upgrade
</button>
query={{ teamId: team.id, docId: currentDoc.id }}
/>
)}
</Flexbox>
<div className='share__row__switch'>
Expand Down Expand Up @@ -650,14 +649,14 @@ const Container = styled.div`
flex: 0 0 auto;
display: flex;
justify-content: center;
align-items:center;
align-items: center;
width: 26px;
padding: 0;
text-align: center;
cursor:pointer;
cursor: pointer;
&:last-child {
border-top-right-radius: 3px;
border-bottom-right-radius: 3px;
border-top-right-radius: 3px;
border-bottom-right-radius: 3px;
}
}
#share__link__copy {
Expand Down Expand Up @@ -689,14 +688,7 @@ const Container = styled.div`
}

.upgrade__badge {
${primaryButtonStyle}
font-size: ${({ theme }) => theme.fontSizes.xxsmall}px;
border-radius: 3px;
height: 26px;
padding: 0 5px;
text-transform: uppercase;
margin-left: ${({ theme }) => theme.space.xsmall}px;
cursor: pointer;
}

.share__row__label span {
Expand Down
8 changes: 3 additions & 5 deletions src/cloud/components/molecules/LayoutSelect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,13 @@ import { MetaKeyText } from '../../lib/keyboard'
import Tooltip from '../atoms/Tooltip'
import styled from '../../lib/styled'
import { usePreferences } from '../../lib/stores/preferences'
import {
trackAction,
ActionTrackTypes,
} from '../../lib/analytics/mixpanelFront'
import {
togglePreviewModeEventEmitter,
toggleSplitEditModeEventEmitter,
} from '../../lib/utils/events'
import { StyledTopBarIcon } from '../organisms/RightSideTopBar/styled'
import { trackEvent } from '../../api/track'
import { MixpanelActionTrackTypes } from '../../interfaces/analytics/mixpanel'

interface LayoutSelectProps {
currentMode: LayoutMode
Expand All @@ -41,7 +39,7 @@ const LayoutSelect = ({

const toggleViewMode = useCallback(() => {
if (currentMode === 'preview') {
trackAction(ActionTrackTypes.DocLayoutEdit, {
trackEvent(MixpanelActionTrackTypes.DocLayoutEdit, {
teamId,
docId,
})
Expand Down
Loading