Skip to content

Commit b69b375

Browse files
authored
Export & Sunset
1 parent bab4d8f commit b69b375

File tree

14 files changed

+317
-30
lines changed

14 files changed

+317
-30
lines changed

src/cloud/api/teams/export.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { callApiBlob } from '../../lib/client'
2+
3+
export async function exportWorkspace(teamId: string) {
4+
const data = await callApiBlob(`api/teams/${teamId}/export`, {
5+
method: 'get',
6+
})
7+
return data
8+
}

src/cloud/components/Application.tsx

Lines changed: 22 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ import { mapUsers } from '../../design/lib/mappers/users'
3030
import {
3131
mdiCog,
3232
mdiDownload,
33-
mdiGiftOutline,
33+
mdiExclamationThick,
3434
mdiInbox,
3535
mdiLogoutVariant,
3636
mdiMagnify,
@@ -49,8 +49,6 @@ import {
4949
import { useModal } from '../../design/lib/stores/modal'
5050
import NewDocButton from './buttons/NewDocButton'
5151
import { useCloudSidebarTree } from '../lib/hooks/sidebar/useCloudSidebarTree'
52-
import { isTimeEligibleForDiscount } from '../lib/subscription'
53-
import DiscountModal from './Modal/contents/DiscountModal'
5452
import { Notification as UserNotification } from '../interfaces/db/notifications'
5553
import useNotificationState from '../../design/lib/hooks/useNotificationState'
5654
import { useNotifications } from '../../design/lib/stores/notifications'
@@ -64,7 +62,6 @@ import SidebarHeader from '../../design/components/organisms/Sidebar/atoms/Sideb
6462
import SidebarButtonList from '../../design/components/organisms/Sidebar/molecules/SidebarButtonList'
6563
import { getTeamLinkHref } from './Link/TeamLink'
6664
import WithPastille from '../../design/components/atoms/WithPastille'
67-
import SidebarButton from '../../design/components/organisms/Sidebar/atoms/SidebarButton'
6865
import CloudGlobalSearch from './CloudGlobalSearch'
6966
import { useCloudSidebarSpaces } from '../lib/hooks/sidebar/useCloudSidebarSpaces'
7067
import { trackEvent } from '../api/track'
@@ -78,6 +75,7 @@ import SidebarSubscriptionCTA from './Subscription/SidebarSubscriptionCTA'
7875
import { isEmpty } from 'lodash'
7976
import LoaderTopbar from '../../design/components/atoms/loaders/LoaderTopbar'
8077
import Icon from '../../design/components/atoms/Icon'
78+
import ExportModal from './Modal/contents/ExportModal'
8179

8280
interface ApplicationProps {
8381
className?: string
@@ -101,7 +99,6 @@ const Application = ({
10199
permissions = [],
102100
currentUserPermissions,
103101
currentUserIsCoreMember,
104-
subscription,
105102
navigatingBetweenPage,
106103
} = usePage()
107104
const { openModal } = useModal()
@@ -406,6 +403,23 @@ const Application = ({
406403
},
407404
id: 'sidebar__button__members',
408405
},
406+
{
407+
label: 'Export your data',
408+
icon: (
409+
<WithPastille>
410+
<Icon size={16} path={mdiExclamationThick} />
411+
</WithPastille>
412+
),
413+
variant: 'transparent',
414+
labelClick: () => {
415+
return openModal(<ExportModal />, {
416+
showCloseIcon: true,
417+
width: 'large',
418+
})
419+
},
420+
id: 'sidebar__button__export',
421+
pastille: counts[team.id] ? counts[team.id] : undefined,
422+
},
409423
]}
410424
>
411425
{currentUserIsCoreMember && <NewDocButton team={team} />}
@@ -422,6 +436,7 @@ const Application = ({
422436
team,
423437
translate,
424438
showSearchScreen,
439+
openModal,
425440
])
426441

427442
const sidebarFooter = useMemo(() => {
@@ -450,31 +465,11 @@ const Application = ({
450465
id: 'sidebar__button__shared',
451466
},
452467
]}
453-
>
454-
{isTimeEligibleForDiscount(team) && subscription == null ? (
455-
<SidebarButton
456-
variant='subtle'
457-
icon={
458-
<WithPastille>
459-
<Icon size={16} path={mdiGiftOutline} />
460-
</WithPastille>
461-
}
462-
id='sidebar__button__promo'
463-
label={translate(lngKeys.SidebarNewUserDiscount)}
464-
labelClick={() => {
465-
trackEvent(MixpanelActionTrackTypes.DiscountSidebar)
466-
return openModal(<DiscountModal />, {
467-
showCloseIcon: true,
468-
width: 'large',
469-
})
470-
}}
471-
/>
472-
) : null}
473-
</SidebarButtonList>
468+
/>
474469
<SidebarSubscriptionCTA />
475470
</>
476471
)
477-
}, [team, translate, pathname, subscription, push, sendToElectron, openModal])
472+
}, [team, translate, pathname, push, sendToElectron])
478473

479474
return (
480475
<>
@@ -535,7 +530,6 @@ const Application = ({
535530
</>
536531
}
537532
/>
538-
539533
<AnnouncementAlert />
540534
<div
541535
id='application__anchor'

src/cloud/components/FolderPage/index.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import { ViewsManager } from '../Views'
3333
import ApplicationPageLoader from '../ApplicationPageLoader'
3434
import LoaderFolderPage from '../../../design/components/atoms/loaders/LoaderFolderPage'
3535
import ViewerDisclaimer from '../ViewerDisclaimer'
36+
import FolderPageExportSection from '../Onboarding/FolderPageExportSection'
3637

3738
const FolderPage = () => {
3839
const { pageFolder, team, currentUserIsCoreMember } = usePage()
@@ -236,6 +237,7 @@ const FolderPage = () => {
236237
</ApplicationTopbar>
237238
<ApplicationContent>
238239
<FolderPageInviteSection />
240+
<FolderPageExportSection />
239241
<ViewerDisclaimer resource='folder' />
240242
<ViewsManager
241243
parent={{ type: 'folder', target: currentFolder }}

src/cloud/components/MarkdownView/index.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -375,6 +375,7 @@ const MarkdownView = ({
375375
sendToElectron,
376376
updateContent,
377377
content,
378+
showLinkOpenWarning,
378379
])
379380

380381
const processorRef = useRef(markdownProcessor)
@@ -412,7 +413,7 @@ const MarkdownView = ({
412413
onRenderRef.current()
413414
}
414415
} catch (err) {
415-
setState({ type: 'error', err })
416+
setState({ type: 'error', err: err as any })
416417
}
417418
},
418419
100,

src/cloud/components/Modal/contents/DiscountModal.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { useMemo, useState } from 'react'
1+
import React, { useMemo } from 'react'
22
import styled from '../../../../design/lib/styled'
33
import Countdown from 'react-countdown'
44
import {
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
import React, { useCallback, useState } from 'react'
2+
import styled from '../../../../design/lib/styled'
3+
import { usePage } from '../../../lib/stores/pageStore'
4+
import { LoadingButton } from '../../../../design/components/atoms/Button'
5+
import { exportWorkspace } from '../../../api/teams/export'
6+
import Flexbox from '../../../../design/components/atoms/Flexbox'
7+
8+
const ExportModal = () => {
9+
const { team } = usePage()
10+
const [sending, setSending] = useState(false)
11+
12+
const handleExportClick = useCallback(async () => {
13+
if (team == null || sending) {
14+
return
15+
}
16+
17+
setSending(true)
18+
try {
19+
const blob = await exportWorkspace(team.id)
20+
21+
const url = window.URL.createObjectURL(blob)
22+
const a = document.createElement('a')
23+
a.href = url
24+
a.download = `space-${team.name}-export.zip`
25+
document.body.appendChild(a)
26+
a.click()
27+
a.remove()
28+
window.URL.revokeObjectURL(url)
29+
} catch (err) {
30+
console.error('Failed to export space', err)
31+
} finally {
32+
setSending(false)
33+
}
34+
}, [team, sending])
35+
36+
if (team == null) {
37+
return null
38+
}
39+
40+
return (
41+
<Container className='export__modal'>
42+
<header className='export__modal__header'>
43+
<div className='export__modal__title'>Export your space data</div>
44+
</header>
45+
<p className='export__modal__description'>
46+
The service for boostnote is planned to be retired at the end of
47+
September. We recommend exporting your space&apos;s data so that you do
48+
not lose any of your information.
49+
</p>
50+
<p>Here is an overview of what can be exported:</p>
51+
<ul>
52+
<li>Public & your accessible private Folders & documents hierarchy</li>
53+
<li>Your Documents&apos; content</li>
54+
<li>Your Documents&apos;attachments</li>
55+
</ul>
56+
57+
<Flexbox justifyContent='center'>
58+
<LoadingButton
59+
disabled={sending || team == null}
60+
spinning={sending}
61+
onClick={handleExportClick}
62+
>
63+
Download ZIP
64+
</LoadingButton>
65+
</Flexbox>
66+
</Container>
67+
)
68+
}
69+
70+
const Container = styled.div`
71+
text-align: center;
72+
.export__modal__subtitle {
73+
span {
74+
font-size: ${({ theme }) => theme.sizes.fonts.md}px;
75+
display: inline-block;
76+
margin-right: ${({ theme }) => theme.sizes.spaces.sm}px;
77+
}
78+
79+
margin-bottom: ${({ theme }) => theme.sizes.spaces.md}px;
80+
}
81+
82+
.export__modal__header {
83+
text-align: center;
84+
}
85+
86+
.export__modal__title {
87+
margin: 0;
88+
margin-top: ${({ theme }) => theme.sizes.spaces.md}px;
89+
font-size: ${({ theme }) => theme.sizes.fonts.l}px;
90+
}
91+
92+
.export__modal__header > * + .export__modal__header > * {
93+
margin-top: ${({ theme }) => theme.sizes.spaces.df}px;
94+
}
95+
96+
.export__modal__description {
97+
margin: ${({ theme }) => theme.sizes.spaces.df}px 0;
98+
text-transform: uppercase;
99+
color: ${({ theme }) => theme.colors.text.subtle};
100+
font-size: ${({ theme }) => theme.sizes.fonts.md};
101+
}
102+
103+
li {
104+
list-style: none;
105+
}
106+
`
107+
108+
export default ExportModal
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
import React from 'react'
2+
import ColoredBlock from '../../../design/components/atoms/ColoredBlock'
3+
import Flexbox from '../../../design/components/atoms/Flexbox'
4+
import styled from '../../../design/lib/styled'
5+
import Button from '../../../design/components/atoms/Button'
6+
import { mdiExport } from '@mdi/js'
7+
import ExportModal from '../Modal/contents/ExportModal'
8+
import { useModal } from '../../../design/lib/stores/modal'
9+
import { ExternalLink } from '../../../design/components/atoms/Link'
10+
11+
const FolderPageExportSection = () => {
12+
const { openModal } = useModal()
13+
14+
return (
15+
<FolderPageExportSectionContainer>
16+
<ColoredBlock variant='danger' className='export__section__block'>
17+
<Flexbox alignItems='baseline' justifyContent='space-between'>
18+
<h5>BoostNote is getting discontinued</h5>
19+
20+
<ExternalLink
21+
href='https://intercom.help/boostnote-for-teams/en/articles/11579215-important-service-termination-notice-for-boost-note'
22+
showIcon={true}
23+
>
24+
Learn more
25+
</ExternalLink>
26+
</Flexbox>
27+
<Flexbox
28+
style={{ justifyContent: 'space-between', alignItems: 'center' }}
29+
>
30+
<p style={{ marginRight: '10px' }}>
31+
We thank you for your continued support. We regret to inform you
32+
that the service will end at the end of September. As such we
33+
recommend for users to export their data to make sure nothing is
34+
being lost.
35+
</p>
36+
<Button
37+
variant='secondary'
38+
iconPath={mdiExport}
39+
onClick={() => {
40+
return openModal(<ExportModal />, {
41+
showCloseIcon: true,
42+
width: 'large',
43+
})
44+
}}
45+
iconSize={16}
46+
>
47+
Download
48+
</Button>
49+
</Flexbox>
50+
</ColoredBlock>
51+
</FolderPageExportSectionContainer>
52+
)
53+
}
54+
55+
const FolderPageExportSectionContainer = styled.div`
56+
margin: ${({ theme }) => theme.sizes.spaces.df}px
57+
${({ theme }) => theme.sizes.spaces.sm}px;
58+
59+
.export__section__block {
60+
input {
61+
color: ${({ theme }) => theme.colors.text.subtle};
62+
}
63+
h5 {
64+
color: ${({ theme }) => theme.colors.text.primary};
65+
margin: ${({ theme }) => theme.sizes.spaces.sm}px 0;
66+
font-size: ${({ theme }) => theme.sizes.fonts.l}px;
67+
}
68+
p {
69+
color: ${({ theme }) => theme.colors.text.primary};
70+
font-size: ${({ theme }) => theme.sizes.fonts.df}px;
71+
margin-bottom: ${({ theme }) => theme.sizes.spaces.sm}px;
72+
}
73+
.form__row__items {
74+
> * {
75+
margin-bottom: ${({ theme }) => theme.sizes.spaces.sm}px;
76+
}
77+
flex-wrap: wrap;
78+
}
79+
}
80+
81+
.link {
82+
color: ${({ theme }) => theme.colors.text.subtle} !important;
83+
}
84+
`
85+
86+
export default FolderPageExportSection

src/cloud/components/WorkspacePage/index.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import { ViewsManager } from '../Views'
1818
import ApplicationPageLoader from '../ApplicationPageLoader'
1919
import LoaderFolderPage from '../../../design/components/atoms/loaders/LoaderFolderPage'
2020
import ViewerDisclaimer from '../ViewerDisclaimer'
21+
import FolderPageExportSection from '../Onboarding/FolderPageExportSection'
2122

2223
const WorkspacePage = ({
2324
workspace: pageWorkspace,
@@ -133,6 +134,7 @@ const WorkspacePage = ({
133134
<ApplicationTopbar controls={topbarControls} />
134135
<ApplicationContent>
135136
<FolderPageInviteSection />
137+
<FolderPageExportSection />
136138
<ViewerDisclaimer resource='folder' />
137139
<ViewsManager
138140
parent={{ type: 'workspace', target: workspace }}

0 commit comments

Comments
 (0)