diff --git a/src/components/App.tsx b/src/components/App.tsx index 1bccd62cd7..6c18ca4441 100644 --- a/src/components/App.tsx +++ b/src/components/App.tsx @@ -29,7 +29,6 @@ import FSNoteDb from '../lib/db/FSNoteDb' import path from 'path' import { useGeneralStatus } from '../lib/generalStatus' import { getFolderItemId } from '../lib/nav' -import AppModeModal from './organisms/AppModeModal' import { useBoostNoteProtocol } from '../lib/protocol' import { useBoostHub, getBoostHubTeamIconUrl } from '../lib/boosthub' import { useDialog, DialogIconTypes } from '../lib/dialog' @@ -50,11 +49,12 @@ import { } from '../lib/events' import { useCheckedFeatures, - featureAppModeSelect, featureBoostHubIntro, } from '../lib/checkedFeatures' import BoostHubIntroModal from '../components/organisms/BoostHubIntroModal' import { useRouteParams } from '../lib/routeParams' +import { useCreateWorkspaceModal } from '../lib/createWorkspaceModal' +import CreateWorkspaceModal from './organisms/CreateWorkspaceModal' const LoadingText = styled.div` margin: 30px; @@ -341,6 +341,11 @@ const App = () => { const { isChecked } = useCheckedFeatures() + const { + showCreateWorkspaceModal, + toggleShowCreateWorkspaceModal, + } = useCreateWorkspaceModal() + return ( { <> {preferences['general.showAppNavigator'] && } - {!isChecked(featureAppModeSelect) ? ( - - ) : ( - !isChecked(featureBoostHubIntro) && + {!isChecked(featureBoostHubIntro) && } + {showCreateWorkspaceModal && ( + )} ) : ( diff --git a/src/components/PreferencesModal/BillingTab.tsx b/src/components/PreferencesModal/BillingTab.tsx deleted file mode 100644 index dcac59d578..0000000000 --- a/src/components/PreferencesModal/BillingTab.tsx +++ /dev/null @@ -1,165 +0,0 @@ -import React, { useState, useEffect } from 'react' -import { - Section, - SectionHeader, - SectionPrimaryButton, - SectionSecondaryButton, - SectionTable, -} from './styled' -import styled from '../../lib/styled' -import { usePreferences } from '../../lib/preferences' -import { getSubscription, Subscription } from '../../lib/accounts' -import LoginButton from '../atoms/LoginButton' -import { openNew } from '../../lib/platform' -import { useTranslation } from 'react-i18next' - -const BillingContent = styled.div` - .billing-lead { - margin-bottom: 24px; - - p, - button { - display: inline-block; - } - button { - margin-left: 24px; - } - } - - .billing-plan { - vertical-align: top; - text-align: center; - } - - .billing-name { - margin-bottom: 8px; - } - - .billing-price { - margin-bottom: 24px; - font-size: 16px; - font-weight: 400; - } - - .billing-extra { - margin-top: 24px; - max-width: 600px; - font-style: italic; - line-height: 1.8; - } -` - -const BillingTab = () => { - const { preferences } = usePreferences() - const user = preferences['general.accounts'][0] - const [subscription, setSubscription] = useState( - undefined - ) - - useEffect(() => { - if (user != null) { - getSubscription(user).then((subscription) => { - setSubscription(subscription) - }) - } - }, [user]) - - const loggedIn = user != null - const hasSubscription = subscription != null - - const { t } = useTranslation() - - return ( -
- {t('billing.billing')} - - {!loggedIn && ( -
-

{t('billing.message')}

-
- )} - - - - - {t('billing.basic')} - $0 - {!hasSubscription && ( - - {t('billing.current')} - - )} - - - {t('billing.premium')} - {t('billing.price')} - {!loggedIn && ( - - {t('general.signin')} - - )} - {loggedIn && ( - - openNew('https://note.boostio.co/subscription') - } - > - {hasSubscription ? 'Manage' : 'Upgrade'} - - )} - - - - - {t('billing.browser')} - ✓ - ✓ - - - {t('billing.desktop')} - ✓ - ✓ - - - {t('billing.mobile')} - ✓ - ✓ - - - {t('billing.sync')} - ✓ - ✓ - - - {t('billing.local')} - ✓ - ✓ - - - {t('billing.cloud')} - ✓ - ✓ - - - {t('billing.storageSize')} - 100MB - 2GB - - - - {loggedIn && ( -
-

{t('billing.addStorageDescription')}

- openNew('https://note.boostio.co/subscription')} - > - {t('billing.addStorage')} - -
- )} -
-
- ) -} - -export default BillingTab diff --git a/src/components/PreferencesModal/GeneralTab.tsx b/src/components/PreferencesModal/GeneralTab.tsx index ad4b8d966f..45e5f55f32 100644 --- a/src/components/PreferencesModal/GeneralTab.tsx +++ b/src/components/PreferencesModal/GeneralTab.tsx @@ -1,11 +1,5 @@ import React, { useCallback } from 'react' -import { - Section, - SectionHeader, - SectionControl, - SectionSelect, - SectionPrimaryButton, -} from './styled' +import { Section, SectionHeader, SectionControl, SectionSelect } from './styled' import { usePreferences, GeneralThemeOptions, @@ -15,9 +9,6 @@ import { } from '../../lib/preferences' import { useTranslation } from 'react-i18next' import { SelectChangeEventHandler } from '../../lib/events' -import { useUsers } from '../../lib/accounts' -import UserInfo from './UserInfo' -import LoginButton from '../atoms/LoginButton' import { useAnalytics, analyticsEvents } from '../../lib/analytics' import { FormCheckItem } from '../atoms/form' import { NoteSortingOptions } from '../../lib/sort' @@ -25,7 +16,6 @@ import NoteSortingOptionsFragment from '../molecules/NoteSortingOptionsFragment' const GeneralTab = () => { const { preferences, setPreferences } = usePreferences() - const [users, { removeUser }] = useUsers() const { report } = useAnalytics() const selectTheme: SelectChangeEventHandler = useCallback( @@ -97,28 +87,6 @@ const GeneralTab = () => { return (
-
- {t('preferences.account')} -
- {users.map((user) => ( - - ))} - {users.length === 0 && ( - - {(loginState) => - loginState !== 'logging-in' ? ( - <>{t('preferences.addAccount')} - ) : ( - <>{t('preferences.loginWorking')} - ) - } - - )} -
-
App Mode { return case 'about': return - case 'billing': - return case 'storage': if (currentStorage != null) { return @@ -200,12 +197,6 @@ const PreferencesModal = () => { active={tab === 'markdown'} setTab={openTab} /> - {content} diff --git a/src/components/organisms/AppNavigator.tsx b/src/components/organisms/AppNavigator.tsx index 1f87ad740b..ff969b3394 100644 --- a/src/components/organisms/AppNavigator.tsx +++ b/src/components/organisms/AppNavigator.tsx @@ -1,4 +1,4 @@ -import React, { useMemo, useCallback, MouseEventHandler } from 'react' +import React, { useMemo, useCallback } from 'react' import styled from '../../lib/styled' import { borderRight, @@ -12,7 +12,6 @@ import { mdiPlus, mdiCircleMedium } from '@mdi/js' import { useRouter } from '../../lib/router' import { useActiveStorageId, useRouteParams } from '../../lib/routeParams' import AppNavigatorStorageItem from '../molecules/AppNavigatorStorageItem' -import { useDialog, DialogIconTypes } from '../../lib/dialog' import { usePreferences } from '../../lib/preferences' import { openContextMenu } from '../../lib/electronOnly' import { osName } from '../../lib/platform' @@ -24,6 +23,7 @@ import { featureBoostHubSignIn, } from '../../lib/checkedFeatures' import { MenuItemConstructorOptions } from 'electron/main' +import { useCreateWorkspaceModal } from '../../lib/createWorkspaceModal' const TopLevelNavigator = () => { const { storageMap } = useDb() @@ -31,7 +31,7 @@ const TopLevelNavigator = () => { const { setPreferences, preferences } = usePreferences() const { generalStatus } = useGeneralStatus() const routeParams = useRouteParams() - const { isChecked, checkFeature } = useCheckedFeatures() + const { isChecked } = useCheckedFeatures() const boostHubUserInfo = preferences['boosthub.user'] @@ -71,12 +71,6 @@ const TopLevelNavigator = () => { }) }, [generalStatus.boostHubTeams, activeBoostHubTeamDomain]) - const goToStorageCreatePage = useCallback(() => { - push(`/app/storages`) - }, [push]) - - const { createStorage } = useDb() - const { prompt } = useDialog() const { setGeneralStatus } = useGeneralStatus() const { requestSignOut } = useBoostHub() @@ -107,26 +101,16 @@ const TopLevelNavigator = () => { menuItems: [ { type: 'normal', - label: 'Create a Note Storage', + label: 'Create a Local Workspace', click: async () => { - prompt({ - title: 'Create a Storage', - message: 'Enter name of a storage to create', - iconType: DialogIconTypes.Question, - submitButtonLabel: 'Create Storage', - onClose: async (value: string | null) => { - if (value == null) return - const storage = await createStorage(value) - push(`/app/storages/${storage.id}/notes`) - }, - }) + push(`/app/storages`) }, }, ...(boostHubUserInfo != null ? ([ { type: 'normal', - label: 'Create a Team', + label: 'Create a Team Workspace', click: async () => { push('/app/boosthub/teams') }, @@ -146,7 +130,7 @@ const TopLevelNavigator = () => { } : { type: 'normal', - label: 'Sign Out Boost Hub', + label: 'Sign Out Team Account', click: signOut, }, { @@ -164,58 +148,10 @@ const TopLevelNavigator = () => { ], }) }, - [boostHubUserInfo, prompt, createStorage, push, setPreferences, signOut] + [boostHubUserInfo, push, setPreferences, signOut] ) - const openNewStorageContextMenu: MouseEventHandler = useCallback( - (event) => { - event.preventDefault() - openContextMenu({ - menuItems: [ - { label: 'Create a Note Storage', click: goToStorageCreatePage }, - ...(boostHubUserInfo == null - ? ([ - { - type: 'separator', - }, - { - label: isChecked(featureBoostHubSignIn) - ? 'Create a Team Account' - : 'Create a Team Account (New)', - click: () => { - checkFeature(featureBoostHubSignIn) - push('/app/boosthub/login') - }, - }, - ] as MenuItemConstructorOptions[]) - : ([ - { - label: 'Create a Team', - click: () => { - push('/app/boosthub/teams') - }, - }, - { - type: 'separator', - }, - { - type: 'normal', - label: 'Sign Out Boost Hub', - click: signOut, - }, - ] as MenuItemConstructorOptions[])), - ], - }) - }, - [ - goToStorageCreatePage, - isChecked, - checkFeature, - push, - boostHubUserInfo, - signOut, - ] - ) + const { toggleShowCreateWorkspaceModal } = useCreateWorkspaceModal() return ( @@ -225,7 +161,10 @@ const TopLevelNavigator = () => { {boostHubTeams} - + {!isChecked(featureBoostHubSignIn) && ( diff --git a/src/components/organisms/CloudStorageCreateForm.tsx b/src/components/organisms/CloudStorageCreateForm.tsx index cbe02ffb97..700e6ee416 100644 --- a/src/components/organisms/CloudStorageCreateForm.tsx +++ b/src/components/organisms/CloudStorageCreateForm.tsx @@ -17,6 +17,7 @@ import { getStorages, CloudStorage, createStorage as createCloudStorage, + useUsers, } from '../../lib/accounts' import { useFirstUser } from '../../lib/preferences' import { useEffectOnce } from 'react-use' @@ -32,6 +33,7 @@ const CloudStorageCreateForm = () => { const { pushMessage } = useToast() const [creating, setCreating] = useState(false) const user = useFirstUser() + const [, { removeUser }] = useUsers() const [fetching, setFetching] = useState(false) const [remoteStorageList, setRemoteStorageList] = useState([]) @@ -199,6 +201,13 @@ const CloudStorageCreateForm = () => { Fetch cloud storage + { + removeUser(user) + }} + > + Sign Out + diff --git a/src/components/organisms/CreateWorkspaceModal.tsx b/src/components/organisms/CreateWorkspaceModal.tsx new file mode 100644 index 0000000000..43b54f9545 --- /dev/null +++ b/src/components/organisms/CreateWorkspaceModal.tsx @@ -0,0 +1,130 @@ +import React, { useCallback } from 'react' +import styled from '../../lib/styled' +import { border, secondaryButtonStyle } from '../../lib/styled/styleFunctions' +import ModalContainer from '../molecules/ModalContainer' +import Icon from '../atoms/Icon' +import { mdiClose } from '@mdi/js' +import Image from '../atoms/Image' +import { useRouter } from '../../lib/router' +import { + useCheckedFeatures, + featureBoostHubSignIn, +} from '../../lib/checkedFeatures' +import { usePreferences } from '../../lib/preferences' + +interface CreateWorkspaceModalProps { + closeModal: () => void +} + +const CreateWorkspaceModal = ({ closeModal }: CreateWorkspaceModalProps) => { + const { push } = useRouter() + const { preferences } = usePreferences() + const { checkFeature } = useCheckedFeatures() + const chooseLocalWorkspace = useCallback(() => { + closeModal() + push(`/app/storages`) + }, [push, closeModal]) + const boosthubUserInfo = preferences['boosthub.user'] + const chooseCloudWorkspace = useCallback(() => { + checkFeature(featureBoostHubSignIn) + closeModal() + if (boosthubUserInfo == null) { + push('/app/boosthub/login') + } else { + push('/app/boosthub/teams') + } + }, [push, checkFeature, closeModal, boosthubUserInfo]) + + return ( + + + +
+

Create Workspace

+
+ + +
+
+
+
+ ) +} + +export default CreateWorkspaceModal + +const StyledContainer = styled.div` + position: relative; + margin: 50px auto; + background-color: ${({ theme }) => theme.navBackgroundColor}; + max-width: 800px; + max-height: 500px; + z-index: 6002; + ${border} + border-radius: 10px; + padding: 15px; + overflow: hidden; + & > .closeButton { + position: absolute; + top: 0; + right: 0; + width: 40px; + height: 40px; + border: none; + background-color: transparent; + cursor: pointer; + box-sizing: border-box; + font-size: 18px; + outline: none; + + transition: color 200ms ease-in-out; + color: ${({ theme }) => theme.navButtonColor}; + &:hover { + color: ${({ theme }) => theme.navButtonHoverColor}; + } + + &:active, + &.active { + color: ${({ theme }) => theme.navButtonActiveColor}; + } + } + .header { + margin: 0 0 15px; + text-align: center; + } + .optionGroup { + display: flex; + gap: 10px; + } + .option { + width: 300px; + height: 300px; + ${secondaryButtonStyle} + border-radius: 10px; + overflow: hidden; + + & > h2 { + margin: 0 10px; + } + } + .option__image { + width: 220px; + height: 220px; + } +` diff --git a/src/components/organisms/ManageCloudStorageForm.tsx b/src/components/organisms/ManageCloudStorageForm.tsx index 608bd5481e..075b2a0910 100644 --- a/src/components/organisms/ManageCloudStorageForm.tsx +++ b/src/components/organisms/ManageCloudStorageForm.tsx @@ -4,6 +4,7 @@ import React, { useCallback, useRef, useMemo, + useEffect, } from 'react' import { FormGroup, @@ -16,13 +17,20 @@ import { } from '../atoms/form' import { PouchNoteStorage } from '../../lib/db/types' import { useDb } from '../../lib/db' -import { renameStorage, deleteStorage } from '../../lib/accounts' +import { + renameStorage, + deleteStorage, + getSubscription, + Subscription, +} from '../../lib/accounts' import { useFirstUser } from '../../lib/preferences' import { useDialog, DialogIconTypes } from '../../lib/dialog' import { useTranslation } from 'react-i18next' import { useToast } from '../../lib/toast' import Spinner from '../atoms/Spinner' import LoginButton from '../atoms/LoginButton' +import { SectionPrimaryButton } from '../PreferencesModal/styled' +import { openNew } from '../../lib/platform' interface ManageCloudStorageFormProps { storage: PouchNoteStorage @@ -103,6 +111,19 @@ const ManageCloudStorageForm = ({ storage }: ManageCloudStorageFormProps) => { const syncing = useMemo(() => { return storage.sync != null }, [storage.sync]) + + const [subscription, setSubscription] = useState( + undefined + ) + + useEffect(() => { + if (user != null) { + getSubscription(user).then((subscription) => { + setSubscription(subscription) + }) + } + }, [user]) + if (user == null) { return ( <> @@ -172,6 +193,16 @@ const ManageCloudStorageForm = ({ storage }: ManageCloudStorageFormProps) => { {updating ? 'Updating...' : 'Update cloud storage name'}
+ + Billing + + openNew('https://note.boostio.co/subscription')} + > + {subscription != null ? 'Manage' : 'Upgrade'} + + + Unlink / Remove cloud storage {foldedDestructiveButtons ? ( diff --git a/src/components/pages/BoostHubTeamsCreatePage.tsx b/src/components/pages/BoostHubTeamsCreatePage.tsx index b7fd587829..421be72115 100644 --- a/src/components/pages/BoostHubTeamsCreatePage.tsx +++ b/src/components/pages/BoostHubTeamsCreatePage.tsx @@ -10,7 +10,7 @@ const BoostHubTeamsCreatePage = () => { diff --git a/src/components/pages/StorageCreatePage.tsx b/src/components/pages/StorageCreatePage.tsx index 7265c0c372..872ac18e2d 100644 --- a/src/components/pages/StorageCreatePage.tsx +++ b/src/components/pages/StorageCreatePage.tsx @@ -5,6 +5,7 @@ import { FormCheckInlineItem, FormCheckList, FormLabel, + FormBlockquote, } from '../atoms/form' import LocalStorageCreateForm from '../organisms/LocalStorageCreateForm' import CloudStorageCreateForm from '../organisms/CloudStorageCreateForm' @@ -41,7 +42,7 @@ const StorageCreatePage = () => { @@ -63,7 +64,7 @@ const StorageCreatePage = () => { checked={storageType === 'local'} onChange={() => setStorageType('local')} > - {t('storage.typeLocal')} + {t('storage.typeLocal')}* { setStorageType('cloud') }} > - {t('storage.typeCloud')} + {t('storage.typeCloud')}* + + * Local and cloud storage type option will be deprecated. Please + consider to use file system option or create a cloud workspace. + diff --git a/src/index.tsx b/src/index.tsx index 847e611ec5..0e9705fb32 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -14,6 +14,7 @@ import { StorageRouterProvider } from './lib/storageRouter' import { SearchModalProvider } from './lib/searchModal' import { CheckedFeaturesProvider } from './lib/checkedFeatures' import { BoostHubStoreProvider } from './lib/boosthub' +import { CreateWorkspaceModalProvider } from './lib/createWorkspaceModal' const CombinedProvider = combineProviders( SearchModalProvider, @@ -27,7 +28,8 @@ const CombinedProvider = combineProviders( RouterProvider, ToastProvider, CheckedFeaturesProvider, - BoostHubStoreProvider + BoostHubStoreProvider, + CreateWorkspaceModalProvider ) function render(Component: typeof App) { diff --git a/src/lib/createWorkspaceModal.ts b/src/lib/createWorkspaceModal.ts new file mode 100644 index 0000000000..b4d79ae1bb --- /dev/null +++ b/src/lib/createWorkspaceModal.ts @@ -0,0 +1,18 @@ +import { useToggle } from 'react-use' +import { createStoreContext } from './context' + +function useCreateWorkspaceModalStore() { + const [showCreateWorkspaceModal, toggleShowCreateWorkspaceModal] = useToggle( + false + ) + + return { + showCreateWorkspaceModal, + toggleShowCreateWorkspaceModal, + } +} + +export const { + StoreProvider: CreateWorkspaceModalProvider, + useStore: useCreateWorkspaceModal, +} = createStoreContext(useCreateWorkspaceModalStore, 'createWorkspaceModal') diff --git a/static/cloud-workspace.svg b/static/cloud-workspace.svg new file mode 100644 index 0000000000..884a217369 --- /dev/null +++ b/static/cloud-workspace.svg @@ -0,0 +1,110 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/local-workspace.svg b/static/local-workspace.svg new file mode 100644 index 0000000000..f514cb37b2 --- /dev/null +++ b/static/local-workspace.svg @@ -0,0 +1,73 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +