Skip to content
Merged

Export #1610

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
8 changes: 8 additions & 0 deletions src/cloud/api/teams/export.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { callApiBlob } from '../../lib/client'

export async function exportWorkspace(teamId: string) {
const data = await callApiBlob(`api/teams/${teamId}/export`, {
method: 'get',
})
return data
}
50 changes: 22 additions & 28 deletions src/cloud/components/Application.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import { mapUsers } from '../../design/lib/mappers/users'
import {
mdiCog,
mdiDownload,
mdiGiftOutline,
mdiExclamationThick,
mdiInbox,
mdiLogoutVariant,
mdiMagnify,
Expand All @@ -49,8 +49,6 @@ import {
import { useModal } from '../../design/lib/stores/modal'
import NewDocButton from './buttons/NewDocButton'
import { useCloudSidebarTree } from '../lib/hooks/sidebar/useCloudSidebarTree'
import { isTimeEligibleForDiscount } from '../lib/subscription'
import DiscountModal from './Modal/contents/DiscountModal'
import { Notification as UserNotification } from '../interfaces/db/notifications'
import useNotificationState from '../../design/lib/hooks/useNotificationState'
import { useNotifications } from '../../design/lib/stores/notifications'
Expand All @@ -64,7 +62,6 @@ import SidebarHeader from '../../design/components/organisms/Sidebar/atoms/Sideb
import SidebarButtonList from '../../design/components/organisms/Sidebar/molecules/SidebarButtonList'
import { getTeamLinkHref } from './Link/TeamLink'
import WithPastille from '../../design/components/atoms/WithPastille'
import SidebarButton from '../../design/components/organisms/Sidebar/atoms/SidebarButton'
import CloudGlobalSearch from './CloudGlobalSearch'
import { useCloudSidebarSpaces } from '../lib/hooks/sidebar/useCloudSidebarSpaces'
import { trackEvent } from '../api/track'
Expand All @@ -78,6 +75,7 @@ import SidebarSubscriptionCTA from './Subscription/SidebarSubscriptionCTA'
import { isEmpty } from 'lodash'
import LoaderTopbar from '../../design/components/atoms/loaders/LoaderTopbar'
import Icon from '../../design/components/atoms/Icon'
import ExportModal from './Modal/contents/ExportModal'

interface ApplicationProps {
className?: string
Expand All @@ -101,7 +99,6 @@ const Application = ({
permissions = [],
currentUserPermissions,
currentUserIsCoreMember,
subscription,
navigatingBetweenPage,
} = usePage()
const { openModal } = useModal()
Expand Down Expand Up @@ -406,6 +403,23 @@ const Application = ({
},
id: 'sidebar__button__members',
},
{
label: 'Export your data',
icon: (
<WithPastille>
<Icon size={16} path={mdiExclamationThick} />
</WithPastille>
),
variant: 'transparent',
labelClick: () => {
return openModal(<ExportModal />, {
showCloseIcon: true,
width: 'large',
})
},
id: 'sidebar__button__export',
pastille: counts[team.id] ? counts[team.id] : undefined,
},
]}
>
{currentUserIsCoreMember && <NewDocButton team={team} />}
Expand All @@ -422,6 +436,7 @@ const Application = ({
team,
translate,
showSearchScreen,
openModal,
])

const sidebarFooter = useMemo(() => {
Expand Down Expand Up @@ -450,31 +465,11 @@ const Application = ({
id: 'sidebar__button__shared',
},
]}
>
{isTimeEligibleForDiscount(team) && subscription == null ? (
<SidebarButton
variant='subtle'
icon={
<WithPastille>
<Icon size={16} path={mdiGiftOutline} />
</WithPastille>
}
id='sidebar__button__promo'
label={translate(lngKeys.SidebarNewUserDiscount)}
labelClick={() => {
trackEvent(MixpanelActionTrackTypes.DiscountSidebar)
return openModal(<DiscountModal />, {
showCloseIcon: true,
width: 'large',
})
}}
/>
) : null}
</SidebarButtonList>
/>
<SidebarSubscriptionCTA />
</>
)
}, [team, translate, pathname, subscription, push, sendToElectron, openModal])
}, [team, translate, pathname, push, sendToElectron])

return (
<>
Expand Down Expand Up @@ -535,7 +530,6 @@ const Application = ({
</>
}
/>

<AnnouncementAlert />
<div
id='application__anchor'
Expand Down
2 changes: 2 additions & 0 deletions src/cloud/components/FolderPage/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import { ViewsManager } from '../Views'
import ApplicationPageLoader from '../ApplicationPageLoader'
import LoaderFolderPage from '../../../design/components/atoms/loaders/LoaderFolderPage'
import ViewerDisclaimer from '../ViewerDisclaimer'
import FolderPageExportSection from '../Onboarding/FolderPageExportSection'

const FolderPage = () => {
const { pageFolder, team, currentUserIsCoreMember } = usePage()
Expand Down Expand Up @@ -236,6 +237,7 @@ const FolderPage = () => {
</ApplicationTopbar>
<ApplicationContent>
<FolderPageInviteSection />
<FolderPageExportSection />
<ViewerDisclaimer resource='folder' />
<ViewsManager
parent={{ type: 'folder', target: currentFolder }}
Expand Down
3 changes: 2 additions & 1 deletion src/cloud/components/MarkdownView/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,7 @@ const MarkdownView = ({
sendToElectron,
updateContent,
content,
showLinkOpenWarning,
])

const processorRef = useRef(markdownProcessor)
Expand Down Expand Up @@ -412,7 +413,7 @@ const MarkdownView = ({
onRenderRef.current()
}
} catch (err) {
setState({ type: 'error', err })
setState({ type: 'error', err: err as any })
}
},
100,
Expand Down
2 changes: 1 addition & 1 deletion src/cloud/components/Modal/contents/DiscountModal.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useMemo, useState } from 'react'
import React, { useMemo } from 'react'
import styled from '../../../../design/lib/styled'
import Countdown from 'react-countdown'
import {
Expand Down
108 changes: 108 additions & 0 deletions src/cloud/components/Modal/contents/ExportModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import React, { useCallback, useState } from 'react'
import styled from '../../../../design/lib/styled'
import { usePage } from '../../../lib/stores/pageStore'
import { LoadingButton } from '../../../../design/components/atoms/Button'
import { exportWorkspace } from '../../../api/teams/export'
import Flexbox from '../../../../design/components/atoms/Flexbox'

const ExportModal = () => {
const { team } = usePage()
const [sending, setSending] = useState(false)

const handleExportClick = useCallback(async () => {
if (team == null || sending) {
return
}

setSending(true)
try {
const blob = await exportWorkspace(team.id)

const url = window.URL.createObjectURL(blob)
const a = document.createElement('a')
a.href = url
a.download = `space-${team.name}-export.zip`
document.body.appendChild(a)
a.click()
a.remove()
window.URL.revokeObjectURL(url)
} catch (err) {
console.error('Failed to export space', err)
} finally {
setSending(false)
}
}, [team, sending])

if (team == null) {
return null
}

return (
<Container className='export__modal'>
<header className='export__modal__header'>
<div className='export__modal__title'>Export your space data</div>
</header>
<p className='export__modal__description'>
The service for boostnote is planned to be retired at the end of
September. We recommend exporting your space&apos;s data so that you do
not lose any of your information.
</p>
<p>Here is an overview of what can be exported:</p>
<ul>
<li>Public & your accessible private Folders & documents hierarchy</li>
<li>Your Documents&apos; content</li>
<li>Your Documents&apos;attachments</li>
</ul>

<Flexbox justifyContent='center'>
<LoadingButton
disabled={sending || team == null}
spinning={sending}
onClick={handleExportClick}
>
Download ZIP
</LoadingButton>
</Flexbox>
</Container>
)
}

const Container = styled.div`
text-align: center;
.export__modal__subtitle {
span {
font-size: ${({ theme }) => theme.sizes.fonts.md}px;
display: inline-block;
margin-right: ${({ theme }) => theme.sizes.spaces.sm}px;
}

margin-bottom: ${({ theme }) => theme.sizes.spaces.md}px;
}

.export__modal__header {
text-align: center;
}

.export__modal__title {
margin: 0;
margin-top: ${({ theme }) => theme.sizes.spaces.md}px;
font-size: ${({ theme }) => theme.sizes.fonts.l}px;
}

.export__modal__header > * + .export__modal__header > * {
margin-top: ${({ theme }) => theme.sizes.spaces.df}px;
}

.export__modal__description {
margin: ${({ theme }) => theme.sizes.spaces.df}px 0;
text-transform: uppercase;
color: ${({ theme }) => theme.colors.text.subtle};
font-size: ${({ theme }) => theme.sizes.fonts.md};
}

li {
list-style: none;
}
`

export default ExportModal
86 changes: 86 additions & 0 deletions src/cloud/components/Onboarding/FolderPageExportSection.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import React from 'react'
import ColoredBlock from '../../../design/components/atoms/ColoredBlock'
import Flexbox from '../../../design/components/atoms/Flexbox'
import styled from '../../../design/lib/styled'
import Button from '../../../design/components/atoms/Button'
import { mdiExport } from '@mdi/js'
import ExportModal from '../Modal/contents/ExportModal'
import { useModal } from '../../../design/lib/stores/modal'
import { ExternalLink } from '../../../design/components/atoms/Link'

const FolderPageExportSection = () => {
const { openModal } = useModal()

return (
<FolderPageExportSectionContainer>
<ColoredBlock variant='danger' className='export__section__block'>
<Flexbox alignItems='baseline' justifyContent='space-between'>
<h5>BoostNote is getting discontinued</h5>

<ExternalLink
href='https://intercom.help/boostnote-for-teams/en/articles/11579215-important-service-termination-notice-for-boost-note'
showIcon={true}
>
Learn more
</ExternalLink>
</Flexbox>
<Flexbox
style={{ justifyContent: 'space-between', alignItems: 'center' }}
>
<p style={{ marginRight: '10px' }}>
We thank you for your continued support. We regret to inform you
that the service will end at the end of September. As such we
recommend for users to export their data to make sure nothing is
being lost.
</p>
<Button
variant='secondary'
iconPath={mdiExport}
onClick={() => {
return openModal(<ExportModal />, {
showCloseIcon: true,
width: 'large',
})
}}
iconSize={16}
>
Download
</Button>
</Flexbox>
</ColoredBlock>
</FolderPageExportSectionContainer>
)
}

const FolderPageExportSectionContainer = styled.div`
margin: ${({ theme }) => theme.sizes.spaces.df}px
${({ theme }) => theme.sizes.spaces.sm}px;

.export__section__block {
input {
color: ${({ theme }) => theme.colors.text.subtle};
}
h5 {
color: ${({ theme }) => theme.colors.text.primary};
margin: ${({ theme }) => theme.sizes.spaces.sm}px 0;
font-size: ${({ theme }) => theme.sizes.fonts.l}px;
}
p {
color: ${({ theme }) => theme.colors.text.primary};
font-size: ${({ theme }) => theme.sizes.fonts.df}px;
margin-bottom: ${({ theme }) => theme.sizes.spaces.sm}px;
}
.form__row__items {
> * {
margin-bottom: ${({ theme }) => theme.sizes.spaces.sm}px;
}
flex-wrap: wrap;
}
}

.link {
color: ${({ theme }) => theme.colors.text.subtle} !important;
}
`

export default FolderPageExportSection
2 changes: 2 additions & 0 deletions src/cloud/components/WorkspacePage/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { ViewsManager } from '../Views'
import ApplicationPageLoader from '../ApplicationPageLoader'
import LoaderFolderPage from '../../../design/components/atoms/loaders/LoaderFolderPage'
import ViewerDisclaimer from '../ViewerDisclaimer'
import FolderPageExportSection from '../Onboarding/FolderPageExportSection'

const WorkspacePage = ({
workspace: pageWorkspace,
Expand Down Expand Up @@ -133,6 +134,7 @@ const WorkspacePage = ({
<ApplicationTopbar controls={topbarControls} />
<ApplicationContent>
<FolderPageInviteSection />
<FolderPageExportSection />
<ViewerDisclaimer resource='folder' />
<ViewsManager
parent={{ type: 'workspace', target: workspace }}
Expand Down
Loading