refactor(web): dependency management (#2386)
This commit is contained in:
@@ -1,8 +1,7 @@
|
||||
import { ApplicationEvent } from '@standardnotes/snjs'
|
||||
import { WebApplication } from '@/Application/WebApplication'
|
||||
import { ViewControllerManager } from '@/Controllers/ViewControllerManager'
|
||||
import { autorun, IReactionDisposer, IReactionPublic } from 'mobx'
|
||||
import { Component } from 'react'
|
||||
import { WebApplication } from '@/Application/WebApplication'
|
||||
|
||||
export type PureComponentState = Partial<Record<string, unknown>>
|
||||
export type PureComponentProps = Partial<Record<string, unknown>>
|
||||
@@ -13,7 +12,7 @@ export abstract class AbstractComponent<P = PureComponentProps, S = PureComponen
|
||||
|
||||
constructor(
|
||||
props: P,
|
||||
protected application: WebApplication,
|
||||
public readonly application: WebApplication,
|
||||
) {
|
||||
super(props)
|
||||
}
|
||||
@@ -40,10 +39,6 @@ export abstract class AbstractComponent<P = PureComponentProps, S = PureComponen
|
||||
this.deinit()
|
||||
}
|
||||
|
||||
public get viewControllerManager(): ViewControllerManager {
|
||||
return this.application.controllers
|
||||
}
|
||||
|
||||
autorun(view: (r: IReactionPublic) => void): void {
|
||||
this.reactionDisposers.push(autorun(view))
|
||||
}
|
||||
|
||||
@@ -1,35 +1,30 @@
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import { ViewControllerManager } from '@/Controllers/ViewControllerManager'
|
||||
import { WebApplication } from '@/Application/WebApplication'
|
||||
import { useCallback, FunctionComponent, KeyboardEventHandler } from 'react'
|
||||
import { WebApplicationGroup } from '@/Application/WebApplicationGroup'
|
||||
import { AccountMenuPane } from './AccountMenuPane'
|
||||
import MenuPaneSelector from './MenuPaneSelector'
|
||||
import { KeyboardKey } from '@standardnotes/ui-services'
|
||||
import { useApplication } from '../ApplicationProvider'
|
||||
|
||||
export type AccountMenuProps = {
|
||||
viewControllerManager: ViewControllerManager
|
||||
application: WebApplication
|
||||
onClickOutside: () => void
|
||||
mainApplicationGroup: WebApplicationGroup
|
||||
}
|
||||
|
||||
const AccountMenu: FunctionComponent<AccountMenuProps> = ({
|
||||
application,
|
||||
viewControllerManager,
|
||||
mainApplicationGroup,
|
||||
}) => {
|
||||
const { currentPane } = viewControllerManager.accountMenuController
|
||||
const AccountMenu: FunctionComponent<AccountMenuProps> = ({ mainApplicationGroup }) => {
|
||||
const application = useApplication()
|
||||
|
||||
const { currentPane } = application.accountMenuController
|
||||
|
||||
const closeAccountMenu = useCallback(() => {
|
||||
viewControllerManager.accountMenuController.closeAccountMenu()
|
||||
}, [viewControllerManager])
|
||||
application.accountMenuController.closeAccountMenu()
|
||||
}, [application])
|
||||
|
||||
const setCurrentPane = useCallback(
|
||||
(pane: AccountMenuPane) => {
|
||||
viewControllerManager.accountMenuController.setCurrentPane(pane)
|
||||
application.accountMenuController.setCurrentPane(pane)
|
||||
},
|
||||
[viewControllerManager],
|
||||
[application],
|
||||
)
|
||||
|
||||
const handleKeyDown: KeyboardEventHandler<HTMLDivElement> = useCallback(
|
||||
@@ -50,8 +45,6 @@ const AccountMenu: FunctionComponent<AccountMenuProps> = ({
|
||||
return (
|
||||
<div id="account-menu" className="sn-component" onKeyDown={handleKeyDown}>
|
||||
<MenuPaneSelector
|
||||
viewControllerManager={viewControllerManager}
|
||||
application={application}
|
||||
mainApplicationGroup={mainApplicationGroup}
|
||||
menuPane={currentPane}
|
||||
setMenuPane={setCurrentPane}
|
||||
|
||||
@@ -1,14 +1,11 @@
|
||||
import { WebApplication } from '@/Application/WebApplication'
|
||||
import { ViewControllerManager } from '@/Controllers/ViewControllerManager'
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import { ChangeEventHandler, FunctionComponent, ReactNode, useCallback, useEffect, useState } from 'react'
|
||||
import Checkbox from '@/Components/Checkbox/Checkbox'
|
||||
import DecoratedInput from '@/Components/Input/DecoratedInput'
|
||||
import Icon from '@/Components/Icon/Icon'
|
||||
import { useApplication } from '../ApplicationProvider'
|
||||
|
||||
type Props = {
|
||||
application: WebApplication
|
||||
viewControllerManager: ViewControllerManager
|
||||
disabled?: boolean
|
||||
onPrivateUsernameModeChange?: (isPrivate: boolean, identifier?: string) => void
|
||||
onStrictSignInChange?: (isStrictSignIn: boolean) => void
|
||||
@@ -17,15 +14,15 @@ type Props = {
|
||||
}
|
||||
|
||||
const AdvancedOptions: FunctionComponent<Props> = ({
|
||||
viewControllerManager,
|
||||
application,
|
||||
disabled = false,
|
||||
onPrivateUsernameModeChange,
|
||||
onStrictSignInChange,
|
||||
onRecoveryCodesChange,
|
||||
children,
|
||||
}) => {
|
||||
const { server, setServer, enableServerOption, setEnableServerOption } = viewControllerManager.accountMenuController
|
||||
const application = useApplication()
|
||||
|
||||
const { server, setServer, enableServerOption, setEnableServerOption } = application.accountMenuController
|
||||
const [showAdvanced, setShowAdvanced] = useState(false)
|
||||
|
||||
const [isPrivateUsername, setIsPrivateUsername] = useState(false)
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
import { STRING_NON_MATCHING_PASSWORDS } from '@/Constants/Strings'
|
||||
import { WebApplication } from '@/Application/WebApplication'
|
||||
import { ViewControllerManager } from '@/Controllers/ViewControllerManager'
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import {
|
||||
FormEventHandler,
|
||||
@@ -17,23 +15,18 @@ import Checkbox from '@/Components/Checkbox/Checkbox'
|
||||
import DecoratedPasswordInput from '@/Components/Input/DecoratedPasswordInput'
|
||||
import Icon from '@/Components/Icon/Icon'
|
||||
import IconButton from '@/Components/Button/IconButton'
|
||||
import { useApplication } from '../ApplicationProvider'
|
||||
|
||||
type Props = {
|
||||
viewControllerManager: ViewControllerManager
|
||||
application: WebApplication
|
||||
setMenuPane: (pane: AccountMenuPane) => void
|
||||
email: string
|
||||
password: string
|
||||
}
|
||||
|
||||
const ConfirmPassword: FunctionComponent<Props> = ({
|
||||
application,
|
||||
viewControllerManager,
|
||||
setMenuPane,
|
||||
email,
|
||||
password,
|
||||
}) => {
|
||||
const { notesAndTagsCount } = viewControllerManager.accountMenuController
|
||||
const ConfirmPassword: FunctionComponent<Props> = ({ setMenuPane, email, password }) => {
|
||||
const application = useApplication()
|
||||
|
||||
const { notesAndTagsCount } = application.accountMenuController
|
||||
const [confirmPassword, setConfirmPassword] = useState('')
|
||||
const [isRegistering, setIsRegistering] = useState(false)
|
||||
const [isEphemeral, setIsEphemeral] = useState(false)
|
||||
@@ -72,8 +65,8 @@ const ConfirmPassword: FunctionComponent<Props> = ({
|
||||
application
|
||||
.register(email, password, isEphemeral, shouldMergeLocal)
|
||||
.then(() => {
|
||||
viewControllerManager.accountMenuController.closeAccountMenu()
|
||||
viewControllerManager.accountMenuController.setCurrentPane(AccountMenuPane.GeneralMenu)
|
||||
application.accountMenuController.closeAccountMenu()
|
||||
application.accountMenuController.setCurrentPane(AccountMenuPane.GeneralMenu)
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(err)
|
||||
@@ -88,7 +81,7 @@ const ConfirmPassword: FunctionComponent<Props> = ({
|
||||
passwordInputRef.current?.focus()
|
||||
}
|
||||
},
|
||||
[viewControllerManager, application, confirmPassword, email, isEphemeral, password, shouldMergeLocal],
|
||||
[application, confirmPassword, email, isEphemeral, password, shouldMergeLocal],
|
||||
)
|
||||
|
||||
const handleKeyDown: KeyboardEventHandler = useCallback(
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import { WebApplication } from '@/Application/WebApplication'
|
||||
import { ViewControllerManager } from '@/Controllers/ViewControllerManager'
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import {
|
||||
FormEventHandler,
|
||||
@@ -20,8 +18,6 @@ import AdvancedOptions from './AdvancedOptions'
|
||||
import HorizontalSeparator from '../Shared/HorizontalSeparator'
|
||||
|
||||
type Props = {
|
||||
viewControllerManager: ViewControllerManager
|
||||
application: WebApplication
|
||||
setMenuPane: (pane: AccountMenuPane) => void
|
||||
email: string
|
||||
setEmail: React.Dispatch<React.SetStateAction<string>>
|
||||
@@ -29,15 +25,7 @@ type Props = {
|
||||
setPassword: React.Dispatch<React.SetStateAction<string>>
|
||||
}
|
||||
|
||||
const CreateAccount: FunctionComponent<Props> = ({
|
||||
viewControllerManager,
|
||||
application,
|
||||
setMenuPane,
|
||||
email,
|
||||
setEmail,
|
||||
password,
|
||||
setPassword,
|
||||
}) => {
|
||||
const CreateAccount: FunctionComponent<Props> = ({ setMenuPane, email, setEmail, password, setPassword }) => {
|
||||
const emailInputRef = useRef<HTMLInputElement>(null)
|
||||
const passwordInputRef = useRef<HTMLInputElement>(null)
|
||||
const [isPrivateUsername, setIsPrivateUsername] = useState(false)
|
||||
@@ -145,11 +133,7 @@ const CreateAccount: FunctionComponent<Props> = ({
|
||||
<Button className="mt-1" label="Next" primary onClick={handleRegisterFormSubmit} fullWidth={true} />
|
||||
</form>
|
||||
<HorizontalSeparator classes="my-2" />
|
||||
<AdvancedOptions
|
||||
application={application}
|
||||
viewControllerManager={viewControllerManager}
|
||||
onPrivateUsernameModeChange={onPrivateUsernameChange}
|
||||
/>
|
||||
<AdvancedOptions onPrivateUsernameModeChange={onPrivateUsernameChange} />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import { WebApplication } from '@/Application/WebApplication'
|
||||
import { ViewControllerManager } from '@/Controllers/ViewControllerManager'
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import Icon from '@/Components/Icon/Icon'
|
||||
import { SyncQueueStrategy } from '@standardnotes/snjs'
|
||||
@@ -14,10 +12,9 @@ import { WebApplicationGroup } from '@/Application/WebApplicationGroup'
|
||||
import { formatLastSyncDate } from '@/Utils/DateUtils'
|
||||
import Spinner from '@/Components/Spinner/Spinner'
|
||||
import { MenuItemIconSize } from '@/Constants/TailwindClassNames'
|
||||
import { useApplication } from '../ApplicationProvider'
|
||||
|
||||
type Props = {
|
||||
viewControllerManager: ViewControllerManager
|
||||
application: WebApplication
|
||||
mainApplicationGroup: WebApplicationGroup
|
||||
setMenuPane: (pane: AccountMenuPane) => void
|
||||
closeMenu: () => void
|
||||
@@ -25,13 +22,9 @@ type Props = {
|
||||
|
||||
const iconClassName = `text-neutral mr-2 ${MenuItemIconSize}`
|
||||
|
||||
const GeneralAccountMenu: FunctionComponent<Props> = ({
|
||||
application,
|
||||
viewControllerManager,
|
||||
setMenuPane,
|
||||
closeMenu,
|
||||
mainApplicationGroup,
|
||||
}) => {
|
||||
const GeneralAccountMenu: FunctionComponent<Props> = ({ setMenuPane, closeMenu, mainApplicationGroup }) => {
|
||||
const application = useApplication()
|
||||
|
||||
const [isSyncingInProgress, setIsSyncingInProgress] = useState(false)
|
||||
const [lastSyncDate, setLastSyncDate] = useState(formatLastSyncDate(application.sync.getLastSyncDate() as Date))
|
||||
|
||||
@@ -58,23 +51,23 @@ const GeneralAccountMenu: FunctionComponent<Props> = ({
|
||||
})
|
||||
}, [application])
|
||||
|
||||
const user = useMemo(() => application.getUser(), [application])
|
||||
const user = useMemo(() => application.sessions.getUser(), [application])
|
||||
|
||||
const openPreferences = useCallback(() => {
|
||||
viewControllerManager.accountMenuController.closeAccountMenu()
|
||||
viewControllerManager.preferencesController.setCurrentPane('account')
|
||||
viewControllerManager.preferencesController.openPreferences()
|
||||
}, [viewControllerManager])
|
||||
application.accountMenuController.closeAccountMenu()
|
||||
application.preferencesController.setCurrentPane('account')
|
||||
application.preferencesController.openPreferences()
|
||||
}, [application])
|
||||
|
||||
const openHelp = useCallback(() => {
|
||||
viewControllerManager.accountMenuController.closeAccountMenu()
|
||||
viewControllerManager.preferencesController.setCurrentPane('help-feedback')
|
||||
viewControllerManager.preferencesController.openPreferences()
|
||||
}, [viewControllerManager])
|
||||
application.accountMenuController.closeAccountMenu()
|
||||
application.preferencesController.setCurrentPane('help-feedback')
|
||||
application.preferencesController.openPreferences()
|
||||
}, [application])
|
||||
|
||||
const signOut = useCallback(() => {
|
||||
viewControllerManager.accountMenuController.setSigningOut(true)
|
||||
}, [viewControllerManager])
|
||||
application.accountMenuController.setSigningOut(true)
|
||||
}, [application])
|
||||
|
||||
const activateRegisterPane = useCallback(() => {
|
||||
setMenuPane(AccountMenuPane.Register)
|
||||
@@ -100,7 +93,7 @@ const GeneralAccountMenu: FunctionComponent<Props> = ({
|
||||
<div className="mb-3 px-3 text-lg text-foreground lg:text-sm">
|
||||
<div>You're signed in as:</div>
|
||||
<div className="wrap my-0.5 font-bold">{user.email}</div>
|
||||
<span className="text-neutral">{application.getHost()}</span>
|
||||
<span className="text-neutral">{application.getHost.execute().getValue()}</span>
|
||||
</div>
|
||||
<div className="mb-2 flex items-start justify-between px-3 text-mobile-menu-item md:text-tablet-menu-item lg:text-menu-item">
|
||||
{isSyncingInProgress ? (
|
||||
@@ -137,16 +130,13 @@ const GeneralAccountMenu: FunctionComponent<Props> = ({
|
||||
</>
|
||||
)}
|
||||
<Menu
|
||||
isOpen={viewControllerManager.accountMenuController.show}
|
||||
isOpen={application.accountMenuController.show}
|
||||
a11yLabel="General account menu"
|
||||
closeMenu={closeMenu}
|
||||
initialFocus={!application.hasAccount() ? CREATE_ACCOUNT_INDEX : SWITCHER_INDEX}
|
||||
>
|
||||
<MenuItemSeparator />
|
||||
<WorkspaceSwitcherOption
|
||||
mainApplicationGroup={mainApplicationGroup}
|
||||
viewControllerManager={viewControllerManager}
|
||||
/>
|
||||
<WorkspaceSwitcherOption mainApplicationGroup={mainApplicationGroup} />
|
||||
<MenuItemSeparator />
|
||||
{user ? (
|
||||
<MenuItem onClick={openPreferences}>
|
||||
@@ -167,8 +157,8 @@ const GeneralAccountMenu: FunctionComponent<Props> = ({
|
||||
)}
|
||||
<MenuItem
|
||||
onClick={() => {
|
||||
viewControllerManager.importModalController.setIsVisible(true)
|
||||
viewControllerManager.accountMenuController.closeAccountMenu()
|
||||
application.importModalController.setIsVisible(true)
|
||||
application.accountMenuController.closeAccountMenu()
|
||||
}}
|
||||
>
|
||||
<Icon type="archive" className={iconClassName} />
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
import { WebApplication } from '@/Application/WebApplication'
|
||||
import { WebApplicationGroup } from '@/Application/WebApplicationGroup'
|
||||
import { ViewControllerManager } from '@/Controllers/ViewControllerManager'
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import { FunctionComponent, useState } from 'react'
|
||||
import { AccountMenuPane } from './AccountMenuPane'
|
||||
@@ -10,22 +8,13 @@ import GeneralAccountMenu from './GeneralAccountMenu'
|
||||
import SignInPane from './SignIn'
|
||||
|
||||
type Props = {
|
||||
viewControllerManager: ViewControllerManager
|
||||
application: WebApplication
|
||||
mainApplicationGroup: WebApplicationGroup
|
||||
menuPane: AccountMenuPane
|
||||
setMenuPane: (pane: AccountMenuPane) => void
|
||||
closeMenu: () => void
|
||||
}
|
||||
|
||||
const MenuPaneSelector: FunctionComponent<Props> = ({
|
||||
application,
|
||||
viewControllerManager,
|
||||
menuPane,
|
||||
setMenuPane,
|
||||
closeMenu,
|
||||
mainApplicationGroup,
|
||||
}) => {
|
||||
const MenuPaneSelector: FunctionComponent<Props> = ({ menuPane, setMenuPane, closeMenu, mainApplicationGroup }) => {
|
||||
const [email, setEmail] = useState('')
|
||||
const [password, setPassword] = useState('')
|
||||
|
||||
@@ -33,22 +22,16 @@ const MenuPaneSelector: FunctionComponent<Props> = ({
|
||||
case AccountMenuPane.GeneralMenu:
|
||||
return (
|
||||
<GeneralAccountMenu
|
||||
viewControllerManager={viewControllerManager}
|
||||
application={application}
|
||||
mainApplicationGroup={mainApplicationGroup}
|
||||
setMenuPane={setMenuPane}
|
||||
closeMenu={closeMenu}
|
||||
/>
|
||||
)
|
||||
case AccountMenuPane.SignIn:
|
||||
return (
|
||||
<SignInPane viewControllerManager={viewControllerManager} application={application} setMenuPane={setMenuPane} />
|
||||
)
|
||||
return <SignInPane setMenuPane={setMenuPane} />
|
||||
case AccountMenuPane.Register:
|
||||
return (
|
||||
<CreateAccount
|
||||
viewControllerManager={viewControllerManager}
|
||||
application={application}
|
||||
setMenuPane={setMenuPane}
|
||||
email={email}
|
||||
setEmail={setEmail}
|
||||
@@ -57,15 +40,7 @@ const MenuPaneSelector: FunctionComponent<Props> = ({
|
||||
/>
|
||||
)
|
||||
case AccountMenuPane.ConfirmPassword:
|
||||
return (
|
||||
<ConfirmPassword
|
||||
viewControllerManager={viewControllerManager}
|
||||
application={application}
|
||||
setMenuPane={setMenuPane}
|
||||
email={email}
|
||||
password={password}
|
||||
/>
|
||||
)
|
||||
return <ConfirmPassword setMenuPane={setMenuPane} email={email} password={password} />
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import { WebApplication } from '@/Application/WebApplication'
|
||||
import { ViewControllerManager } from '@/Controllers/ViewControllerManager'
|
||||
import { isDev } from '@/Utils'
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import React, { FunctionComponent, KeyboardEventHandler, useCallback, useEffect, useRef, useState } from 'react'
|
||||
@@ -13,15 +11,16 @@ import IconButton from '@/Components/Button/IconButton'
|
||||
import AdvancedOptions from './AdvancedOptions'
|
||||
import HorizontalSeparator from '../Shared/HorizontalSeparator'
|
||||
import { getErrorFromErrorResponse, isErrorResponse } from '@standardnotes/snjs'
|
||||
import { useApplication } from '../ApplicationProvider'
|
||||
|
||||
type Props = {
|
||||
viewControllerManager: ViewControllerManager
|
||||
application: WebApplication
|
||||
setMenuPane: (pane: AccountMenuPane) => void
|
||||
}
|
||||
|
||||
const SignInPane: FunctionComponent<Props> = ({ application, viewControllerManager, setMenuPane }) => {
|
||||
const { notesAndTagsCount } = viewControllerManager.accountMenuController
|
||||
const SignInPane: FunctionComponent<Props> = ({ setMenuPane }) => {
|
||||
const application = useApplication()
|
||||
|
||||
const { notesAndTagsCount } = application.accountMenuController
|
||||
const [email, setEmail] = useState('')
|
||||
const [password, setPassword] = useState('')
|
||||
const [recoveryCodes, setRecoveryCodes] = useState('')
|
||||
@@ -101,7 +100,7 @@ const SignInPane: FunctionComponent<Props> = ({ application, viewControllerManag
|
||||
if (isErrorResponse(response)) {
|
||||
throw new Error(getErrorFromErrorResponse(response).message)
|
||||
}
|
||||
viewControllerManager.accountMenuController.closeAccountMenu()
|
||||
application.accountMenuController.closeAccountMenu()
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(err)
|
||||
@@ -112,7 +111,7 @@ const SignInPane: FunctionComponent<Props> = ({ application, viewControllerManag
|
||||
.finally(() => {
|
||||
setIsSigningIn(false)
|
||||
})
|
||||
}, [viewControllerManager, application, email, isEphemeral, isStrictSignin, password, shouldMergeLocal])
|
||||
}, [application, email, isEphemeral, isStrictSignin, password, shouldMergeLocal])
|
||||
|
||||
const recoverySignIn = useCallback(() => {
|
||||
setIsSigningIn(true)
|
||||
@@ -129,7 +128,7 @@ const SignInPane: FunctionComponent<Props> = ({ application, viewControllerManag
|
||||
if (result.isFailed()) {
|
||||
throw new Error(result.getError())
|
||||
}
|
||||
viewControllerManager.accountMenuController.closeAccountMenu()
|
||||
application.accountMenuController.closeAccountMenu()
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(err)
|
||||
@@ -140,7 +139,7 @@ const SignInPane: FunctionComponent<Props> = ({ application, viewControllerManag
|
||||
.finally(() => {
|
||||
setIsSigningIn(false)
|
||||
})
|
||||
}, [viewControllerManager, application, email, password, recoveryCodes])
|
||||
}, [application, email, password, recoveryCodes])
|
||||
|
||||
const onPrivateUsernameChange = useCallback(
|
||||
(newisPrivateUsername: boolean, privateUsernameIdentifier?: string) => {
|
||||
@@ -251,8 +250,6 @@ const SignInPane: FunctionComponent<Props> = ({ application, viewControllerManag
|
||||
</div>
|
||||
<HorizontalSeparator classes="my-2" />
|
||||
<AdvancedOptions
|
||||
viewControllerManager={viewControllerManager}
|
||||
application={application}
|
||||
disabled={isSigningIn}
|
||||
onPrivateUsernameModeChange={onPrivateUsernameChange}
|
||||
onStrictSignInChange={handleStrictSigninChange}
|
||||
|
||||
@@ -1,25 +1,20 @@
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import { ViewControllerManager } from '@/Controllers/ViewControllerManager'
|
||||
import { WebApplication } from '@/Application/WebApplication'
|
||||
import { User as UserType } from '@standardnotes/snjs'
|
||||
import { useApplication } from '../ApplicationProvider'
|
||||
|
||||
type Props = {
|
||||
viewControllerManager: ViewControllerManager
|
||||
application: WebApplication
|
||||
}
|
||||
const User = () => {
|
||||
const application = useApplication()
|
||||
|
||||
const User = ({ viewControllerManager, application }: Props) => {
|
||||
const { server } = viewControllerManager.accountMenuController
|
||||
const user = application.getUser() as UserType
|
||||
const { server } = application.accountMenuController
|
||||
const user = application.sessions.getUser() as UserType
|
||||
|
||||
return (
|
||||
<div className="sk-panel-section">
|
||||
{viewControllerManager.syncStatusController.errorMessage && (
|
||||
{application.syncStatusController.errorMessage && (
|
||||
<div className="sk-notification danger">
|
||||
<div className="sk-notification-title">Sync Unreachable</div>
|
||||
<div className="sk-notification-text">
|
||||
Hmm...we can't seem to sync your account. The reason:{' '}
|
||||
{viewControllerManager.syncStatusController.errorMessage}
|
||||
Hmm...we can't seem to sync your account. The reason: {application.syncStatusController.errorMessage}
|
||||
</div>
|
||||
<a
|
||||
className="sk-a info-contrast sk-bold sk-panel-row"
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { WebApplicationGroup } from '@/Application/WebApplicationGroup'
|
||||
import { ViewControllerManager } from '@/Controllers/ViewControllerManager'
|
||||
import { ApplicationDescriptor, ApplicationGroupEvent, ButtonType } from '@standardnotes/snjs'
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import { FunctionComponent, useCallback, useEffect, useState } from 'react'
|
||||
@@ -8,20 +7,21 @@ import Menu from '@/Components/Menu/Menu'
|
||||
import MenuItem from '@/Components/Menu/MenuItem'
|
||||
import MenuItemSeparator from '@/Components/Menu/MenuItemSeparator'
|
||||
import WorkspaceMenuItem from './WorkspaceMenuItem'
|
||||
import { useApplication } from '@/Components/ApplicationProvider'
|
||||
|
||||
type Props = {
|
||||
mainApplicationGroup: WebApplicationGroup
|
||||
viewControllerManager: ViewControllerManager
|
||||
isOpen: boolean
|
||||
hideWorkspaceOptions?: boolean
|
||||
}
|
||||
|
||||
const WorkspaceSwitcherMenu: FunctionComponent<Props> = ({
|
||||
mainApplicationGroup,
|
||||
viewControllerManager,
|
||||
isOpen,
|
||||
hideWorkspaceOptions = false,
|
||||
}: Props) => {
|
||||
const application = useApplication()
|
||||
|
||||
const [applicationDescriptors, setApplicationDescriptors] = useState<ApplicationDescriptor[]>(
|
||||
mainApplicationGroup.getDescriptors(),
|
||||
)
|
||||
@@ -43,7 +43,7 @@ const WorkspaceSwitcherMenu: FunctionComponent<Props> = ({
|
||||
}, [mainApplicationGroup])
|
||||
|
||||
const signoutAll = useCallback(async () => {
|
||||
const confirmed = await viewControllerManager.application.alerts.confirm(
|
||||
const confirmed = await application.alerts.confirm(
|
||||
'Are you sure you want to sign out of all workspaces on this device?',
|
||||
undefined,
|
||||
'Sign out all',
|
||||
@@ -53,11 +53,11 @@ const WorkspaceSwitcherMenu: FunctionComponent<Props> = ({
|
||||
return
|
||||
}
|
||||
mainApplicationGroup.signOutAllWorkspaces().catch(console.error)
|
||||
}, [mainApplicationGroup, viewControllerManager])
|
||||
}, [mainApplicationGroup, application])
|
||||
|
||||
const destroyWorkspace = useCallback(() => {
|
||||
viewControllerManager.accountMenuController.setSigningOut(true)
|
||||
}, [viewControllerManager])
|
||||
application.accountMenuController.setSigningOut(true)
|
||||
}, [application])
|
||||
|
||||
const activateWorkspace = useCallback(
|
||||
async (descriptor: ApplicationDescriptor) => {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { FOCUSABLE_BUT_NOT_TABBABLE } from '@/Constants/Constants'
|
||||
import { WebApplicationGroup } from '@/Application/WebApplicationGroup'
|
||||
import { ViewControllerManager } from '@/Controllers/ViewControllerManager'
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import { FunctionComponent, useCallback, useRef, useState } from 'react'
|
||||
import Icon from '@/Components/Icon/Icon'
|
||||
@@ -11,10 +10,9 @@ import { MenuItemIconSize } from '@/Constants/TailwindClassNames'
|
||||
|
||||
type Props = {
|
||||
mainApplicationGroup: WebApplicationGroup
|
||||
viewControllerManager: ViewControllerManager
|
||||
}
|
||||
|
||||
const WorkspaceSwitcherOption: FunctionComponent<Props> = ({ mainApplicationGroup, viewControllerManager }) => {
|
||||
const WorkspaceSwitcherOption: FunctionComponent<Props> = ({ mainApplicationGroup }) => {
|
||||
const buttonRef = useRef<HTMLButtonElement>(null)
|
||||
const [isOpen, setIsOpen] = useState(false)
|
||||
|
||||
@@ -40,11 +38,7 @@ const WorkspaceSwitcherOption: FunctionComponent<Props> = ({ mainApplicationGrou
|
||||
side="right"
|
||||
togglePopover={toggleMenu}
|
||||
>
|
||||
<WorkspaceSwitcherMenu
|
||||
mainApplicationGroup={mainApplicationGroup}
|
||||
viewControllerManager={viewControllerManager}
|
||||
isOpen={isOpen}
|
||||
/>
|
||||
<WorkspaceSwitcherMenu mainApplicationGroup={mainApplicationGroup} isOpen={isOpen} />
|
||||
</Popover>
|
||||
</>
|
||||
)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { WebApplicationGroup } from '@/Application/WebApplicationGroup'
|
||||
import { getPlatformString, isIOS } from '@/Utils'
|
||||
import { getPlatformString } from '@/Utils'
|
||||
import { ApplicationEvent, Challenge, removeFromArray, WebAppEvent } from '@standardnotes/snjs'
|
||||
import { alertDialog, RouteType } from '@standardnotes/ui-services'
|
||||
import { alertDialog, isIOS, RouteType } from '@standardnotes/ui-services'
|
||||
import { WebApplication } from '@/Application/WebApplication'
|
||||
import Footer from '@/Components/Footer/Footer'
|
||||
import SessionsModal from '@/Components/SessionsModal/SessionsModal'
|
||||
@@ -30,6 +30,7 @@ import LinkingControllerProvider from '@/Controllers/LinkingControllerProvider'
|
||||
import ImportModal from '../ImportModal/ImportModal'
|
||||
import IosKeyboardClose from '../IosKeyboardClose/IosKeyboardClose'
|
||||
import EditorWidthSelectionModalWrapper from '../EditorWidthSelectionModal/EditorWidthSelectionModal'
|
||||
import { ProtectionEvent } from '@standardnotes/services'
|
||||
|
||||
type Props = {
|
||||
application: WebApplication
|
||||
@@ -47,10 +48,8 @@ const ApplicationView: FunctionComponent<Props> = ({ application, mainApplicatio
|
||||
const currentWriteErrorDialog = useRef<Promise<void> | null>(null)
|
||||
const currentLoadErrorDialog = useRef<Promise<void> | null>(null)
|
||||
|
||||
const viewControllerManager = application.controllers
|
||||
|
||||
useEffect(() => {
|
||||
const desktopService = application.getDesktopService()
|
||||
const desktopService = application.desktopManager
|
||||
|
||||
if (desktopService) {
|
||||
application.componentManager.setDesktopManager(desktopService)
|
||||
@@ -142,10 +141,6 @@ const ApplicationView: FunctionComponent<Props> = ({ application, mainApplicatio
|
||||
})
|
||||
.catch(console.error)
|
||||
}
|
||||
} else if (eventName === ApplicationEvent.BiometricsSoftLockEngaged) {
|
||||
setNeedsUnlock(true)
|
||||
} else if (eventName === ApplicationEvent.BiometricsSoftLockDisengaged) {
|
||||
setNeedsUnlock(false)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -154,10 +149,22 @@ const ApplicationView: FunctionComponent<Props> = ({ application, mainApplicatio
|
||||
}
|
||||
}, [application, onAppLaunch, onAppStart])
|
||||
|
||||
useEffect(() => {
|
||||
const disposer = application.protections.addEventObserver(async (eventName) => {
|
||||
if (eventName === ProtectionEvent.BiometricsSoftLockEngaged) {
|
||||
setNeedsUnlock(true)
|
||||
} else if (eventName === ProtectionEvent.BiometricsSoftLockDisengaged) {
|
||||
setNeedsUnlock(false)
|
||||
}
|
||||
})
|
||||
|
||||
return disposer
|
||||
}, [application])
|
||||
|
||||
useEffect(() => {
|
||||
const removeObserver = application.addWebEventObserver(async (eventName) => {
|
||||
if (eventName === WebAppEvent.WindowDidFocus) {
|
||||
if (!(await application.isLocked())) {
|
||||
if (!(await application.protections.isLocked())) {
|
||||
application.sync.sync().catch(console.error)
|
||||
}
|
||||
}
|
||||
@@ -178,14 +185,13 @@ const ApplicationView: FunctionComponent<Props> = ({ application, mainApplicatio
|
||||
<ChallengeModal
|
||||
key={`${challenge.id}${application.ephemeralIdentifier}`}
|
||||
application={application}
|
||||
viewControllerManager={viewControllerManager}
|
||||
mainApplicationGroup={mainApplicationGroup}
|
||||
challenge={challenge}
|
||||
onDismiss={removeChallenge}
|
||||
/>
|
||||
</div>
|
||||
))
|
||||
}, [viewControllerManager, challenges, mainApplicationGroup, removeChallenge, application])
|
||||
}, [challenges, mainApplicationGroup, removeChallenge, application])
|
||||
|
||||
if (!renderAppContents) {
|
||||
return <AndroidBackHandlerProvider application={application}>{renderChallenges()}</AndroidBackHandlerProvider>
|
||||
@@ -198,23 +204,13 @@ const ApplicationView: FunctionComponent<Props> = ({ application, mainApplicatio
|
||||
<ApplicationProvider application={application}>
|
||||
<CommandProvider service={application.keyboardService}>
|
||||
<AndroidBackHandlerProvider application={application}>
|
||||
<ResponsivePaneProvider paneController={application.controllers.paneController}>
|
||||
<PremiumModalProvider
|
||||
application={application}
|
||||
featuresController={viewControllerManager.featuresController}
|
||||
>
|
||||
<LinkingControllerProvider controller={viewControllerManager.linkingController}>
|
||||
<FileDragNDropProvider
|
||||
application={application}
|
||||
featuresController={viewControllerManager.featuresController}
|
||||
filesController={viewControllerManager.filesController}
|
||||
>
|
||||
<LazyLoadedClipperView
|
||||
viewControllerManager={viewControllerManager}
|
||||
applicationGroup={mainApplicationGroup}
|
||||
/>
|
||||
<ResponsivePaneProvider paneController={application.paneController}>
|
||||
<PremiumModalProvider application={application}>
|
||||
<LinkingControllerProvider controller={application.linkingController}>
|
||||
<FileDragNDropProvider application={application}>
|
||||
<LazyLoadedClipperView applicationGroup={mainApplicationGroup} />
|
||||
<ToastContainer />
|
||||
<FilePreviewModalWrapper application={application} viewControllerManager={viewControllerManager} />
|
||||
<FilePreviewModalWrapper application={application} />
|
||||
{renderChallenges()}
|
||||
</FileDragNDropProvider>
|
||||
</LinkingControllerProvider>
|
||||
@@ -230,64 +226,38 @@ const ApplicationView: FunctionComponent<Props> = ({ application, mainApplicatio
|
||||
<ApplicationProvider application={application}>
|
||||
<CommandProvider service={application.keyboardService}>
|
||||
<AndroidBackHandlerProvider application={application}>
|
||||
<ResponsivePaneProvider paneController={application.controllers.paneController}>
|
||||
<PremiumModalProvider
|
||||
application={application}
|
||||
featuresController={viewControllerManager.featuresController}
|
||||
>
|
||||
<LinkingControllerProvider controller={viewControllerManager.linkingController}>
|
||||
<ResponsivePaneProvider paneController={application.paneController}>
|
||||
<PremiumModalProvider application={application}>
|
||||
<LinkingControllerProvider controller={application.linkingController}>
|
||||
<div className={platformString + ' main-ui-view sn-component h-full'}>
|
||||
<FileDragNDropProvider
|
||||
application={application}
|
||||
featuresController={viewControllerManager.featuresController}
|
||||
filesController={viewControllerManager.filesController}
|
||||
>
|
||||
<FileDragNDropProvider application={application}>
|
||||
<PanesSystemComponent />
|
||||
</FileDragNDropProvider>
|
||||
<>
|
||||
<Footer application={application} applicationGroup={mainApplicationGroup} />
|
||||
<SessionsModal application={application} viewControllerManager={viewControllerManager} />
|
||||
<PreferencesViewWrapper viewControllerManager={viewControllerManager} application={application} />
|
||||
<RevisionHistoryModal
|
||||
application={application}
|
||||
historyModalController={viewControllerManager.historyModalController}
|
||||
selectionController={viewControllerManager.selectionController}
|
||||
/>
|
||||
<SessionsModal application={application} />
|
||||
<PreferencesViewWrapper application={application} />
|
||||
<RevisionHistoryModal application={application} />
|
||||
</>
|
||||
{renderChallenges()}
|
||||
<>
|
||||
<NotesContextMenu
|
||||
navigationController={viewControllerManager.navigationController}
|
||||
notesController={viewControllerManager.notesController}
|
||||
linkingController={viewControllerManager.linkingController}
|
||||
historyModalController={viewControllerManager.historyModalController}
|
||||
selectionController={viewControllerManager.selectionController}
|
||||
/>
|
||||
<NotesContextMenu />
|
||||
<TagContextMenuWrapper
|
||||
navigationController={viewControllerManager.navigationController}
|
||||
featuresController={viewControllerManager.featuresController}
|
||||
navigationController={application.navigationController}
|
||||
featuresController={application.featuresController}
|
||||
/>
|
||||
<FileContextMenuWrapper
|
||||
filesController={viewControllerManager.filesController}
|
||||
selectionController={viewControllerManager.selectionController}
|
||||
navigationController={viewControllerManager.navigationController}
|
||||
linkingController={viewControllerManager.linkingController}
|
||||
/>
|
||||
<PurchaseFlowWrapper application={application} viewControllerManager={viewControllerManager} />
|
||||
<ConfirmSignoutContainer
|
||||
applicationGroup={mainApplicationGroup}
|
||||
viewControllerManager={viewControllerManager}
|
||||
application={application}
|
||||
filesController={application.filesController}
|
||||
itemListController={application.itemListController}
|
||||
/>
|
||||
<PurchaseFlowWrapper application={application} />
|
||||
<ConfirmSignoutContainer applicationGroup={mainApplicationGroup} application={application} />
|
||||
<ToastContainer />
|
||||
<FilePreviewModalWrapper application={application} viewControllerManager={viewControllerManager} />
|
||||
<FilePreviewModalWrapper application={application} />
|
||||
<PermissionsModalWrapper application={application} />
|
||||
<EditorWidthSelectionModalWrapper />
|
||||
<ConfirmDeleteAccountContainer
|
||||
application={application}
|
||||
viewControllerManager={viewControllerManager}
|
||||
/>
|
||||
<ImportModal importModalController={viewControllerManager.importModalController} />
|
||||
<ConfirmDeleteAccountContainer application={application} />
|
||||
<ImportModal importModalController={application.importModalController} />
|
||||
</>
|
||||
{application.routeService.isDotOrg && <DotOrgNotice />}
|
||||
{isIOS() && <IosKeyboardClose />}
|
||||
|
||||
@@ -22,7 +22,7 @@ const BiometricsPrompt = ({ application, onValueChange, prompt, buttonRef }: Pro
|
||||
fullWidth
|
||||
colorStyle={authenticated ? 'success' : 'info'}
|
||||
onClick={async () => {
|
||||
const authenticated = await application.mobileDevice().authenticateWithBiometrics()
|
||||
const authenticated = await application.mobileDevice.authenticateWithBiometrics()
|
||||
setAuthenticated(authenticated)
|
||||
onValueChange(authenticated, prompt)
|
||||
}}
|
||||
|
||||
@@ -15,7 +15,6 @@ import Icon from '@/Components/Icon/Icon'
|
||||
import ChallengeModalPrompt from './ChallengePrompt'
|
||||
import LockscreenWorkspaceSwitcher from './LockscreenWorkspaceSwitcher'
|
||||
import { WebApplicationGroup } from '@/Application/WebApplicationGroup'
|
||||
import { ViewControllerManager } from '@/Controllers/ViewControllerManager'
|
||||
import { ChallengeModalValues } from './ChallengeModalValues'
|
||||
import { InputValue } from './InputValue'
|
||||
import { classNames } from '@standardnotes/utils'
|
||||
@@ -26,7 +25,6 @@ import { useAutoElementRect } from '@/Hooks/useElementRect'
|
||||
|
||||
type Props = {
|
||||
application: WebApplication
|
||||
viewControllerManager: ViewControllerManager
|
||||
mainApplicationGroup: WebApplicationGroup
|
||||
challenge: Challenge
|
||||
onDismiss?: (challenge: Challenge) => void
|
||||
@@ -48,13 +46,7 @@ const validateValues = (values: ChallengeModalValues, prompts: ChallengePrompt[]
|
||||
return undefined
|
||||
}
|
||||
|
||||
const ChallengeModal: FunctionComponent<Props> = ({
|
||||
application,
|
||||
viewControllerManager,
|
||||
mainApplicationGroup,
|
||||
challenge,
|
||||
onDismiss,
|
||||
}) => {
|
||||
const ChallengeModal: FunctionComponent<Props> = ({ application, mainApplicationGroup, challenge, onDismiss }) => {
|
||||
const promptsContainerRef = useRef<HTMLFormElement>(null)
|
||||
|
||||
const [values, setValues] = useState<ChallengeModalValues>(() => {
|
||||
@@ -341,12 +333,7 @@ const ChallengeModal: FunctionComponent<Props> = ({
|
||||
Forgot passcode?
|
||||
</Button>
|
||||
)}
|
||||
{shouldShowWorkspaceSwitcher && (
|
||||
<LockscreenWorkspaceSwitcher
|
||||
mainApplicationGroup={mainApplicationGroup}
|
||||
viewControllerManager={viewControllerManager}
|
||||
/>
|
||||
)}
|
||||
{shouldShowWorkspaceSwitcher && <LockscreenWorkspaceSwitcher mainApplicationGroup={mainApplicationGroup} />}
|
||||
</div>
|
||||
</Modal>
|
||||
</ModalOverlay>
|
||||
|
||||
@@ -39,7 +39,7 @@ const ChallengeModalPrompt: FunctionComponent<Props> = ({
|
||||
const activatePrompt = useCallback(async () => {
|
||||
if (prompt.validation === ChallengeValidation.Biometric) {
|
||||
if (application.isNativeMobileWeb()) {
|
||||
const appState = await application.mobileDevice().getAppState()
|
||||
const appState = await application.mobileDevice.getAppState()
|
||||
|
||||
if (appState !== 'active') {
|
||||
return
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { WebApplicationGroup } from '@/Application/WebApplicationGroup'
|
||||
import { ViewControllerManager } from '@/Controllers/ViewControllerManager'
|
||||
import { FunctionComponent, useCallback, useRef, useState } from 'react'
|
||||
import WorkspaceSwitcherMenu from '@/Components/AccountMenu/WorkspaceSwitcher/WorkspaceSwitcherMenu'
|
||||
import Button from '@/Components/Button/Button'
|
||||
@@ -8,10 +7,9 @@ import Popover from '../Popover/Popover'
|
||||
|
||||
type Props = {
|
||||
mainApplicationGroup: WebApplicationGroup
|
||||
viewControllerManager: ViewControllerManager
|
||||
}
|
||||
|
||||
const LockscreenWorkspaceSwitcher: FunctionComponent<Props> = ({ mainApplicationGroup, viewControllerManager }) => {
|
||||
const LockscreenWorkspaceSwitcher: FunctionComponent<Props> = ({ mainApplicationGroup }) => {
|
||||
const buttonRef = useRef<HTMLButtonElement>(null)
|
||||
const containerRef = useRef<HTMLDivElement>(null)
|
||||
const [isOpen, setIsOpen] = useState(false)
|
||||
@@ -38,7 +36,6 @@ const LockscreenWorkspaceSwitcher: FunctionComponent<Props> = ({ mainApplication
|
||||
>
|
||||
<WorkspaceSwitcherMenu
|
||||
mainApplicationGroup={mainApplicationGroup}
|
||||
viewControllerManager={viewControllerManager}
|
||||
isOpen={isOpen}
|
||||
hideWorkspaceOptions={true}
|
||||
/>
|
||||
|
||||
@@ -3,13 +3,13 @@ import { ChallengePrompt } from '@standardnotes/services'
|
||||
import { RefObject, useState } from 'react'
|
||||
|
||||
import { WebApplication } from '@/Application/WebApplication'
|
||||
import { isAndroid } from '@/Utils'
|
||||
|
||||
import Button from '../Button/Button'
|
||||
import Icon from '../Icon/Icon'
|
||||
|
||||
import { InputValue } from './InputValue'
|
||||
import U2FPromptIframeContainer from './U2FPromptIframeContainer'
|
||||
import { isAndroid } from '@standardnotes/ui-services'
|
||||
|
||||
type Props = {
|
||||
application: WebApplication
|
||||
@@ -27,7 +27,7 @@ const U2FPrompt = ({ application, onValueChange, prompt, buttonRef, contextData
|
||||
return (
|
||||
<U2FPromptIframeContainer
|
||||
contextData={contextData}
|
||||
apiHost={application.getHost() || window.defaultSyncServer}
|
||||
apiHost={application.getHost.execute().getValue() || window.defaultSyncServer}
|
||||
onResponse={(response) => {
|
||||
onValueChange(response, prompt)
|
||||
}}
|
||||
@@ -60,9 +60,9 @@ const U2FPrompt = ({ application, onValueChange, prompt, buttonRef, contextData
|
||||
}
|
||||
const authenticatorOptions = authenticatorOptionsOrError.getValue()
|
||||
|
||||
authenticatorResponse = await application
|
||||
.mobileDevice()
|
||||
.authenticateWithU2F(JSON.stringify(authenticatorOptions))
|
||||
authenticatorResponse = await application.mobileDevice.authenticateWithU2F(
|
||||
JSON.stringify(authenticatorOptions),
|
||||
)
|
||||
} else {
|
||||
const authenticatorResponseOrError = await application.getAuthenticatorAuthenticationResponse.execute({
|
||||
username: username.value,
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { ViewControllerManager } from '@/Controllers/ViewControllerManager'
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import { FunctionComponent, useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||
import ChangeEditorMenu from './ChangeEditorMenu'
|
||||
@@ -11,19 +10,14 @@ import { NoteViewController } from '../NoteView/Controller/NoteViewController'
|
||||
import { NoteType, noteTypeForEditorIdentifier } from '@standardnotes/snjs'
|
||||
|
||||
type Props = {
|
||||
viewControllerManager: ViewControllerManager
|
||||
noteViewController?: NoteViewController
|
||||
onClickPreprocessing?: () => Promise<void>
|
||||
}
|
||||
|
||||
const ChangeEditorButton: FunctionComponent<Props> = ({
|
||||
viewControllerManager,
|
||||
noteViewController,
|
||||
onClickPreprocessing,
|
||||
}: Props) => {
|
||||
const ChangeEditorButton: FunctionComponent<Props> = ({ noteViewController, onClickPreprocessing }: Props) => {
|
||||
const application = useApplication()
|
||||
|
||||
const note = viewControllerManager.notesController.firstSelectedNote
|
||||
const note = application.notesController.firstSelectedNote
|
||||
const [isOpen, setIsOpen] = useState(false)
|
||||
const buttonRef = useRef<HTMLButtonElement>(null)
|
||||
const containerRef = useRef<HTMLDivElement>(null)
|
||||
|
||||
@@ -85,14 +85,14 @@ const ChangeEditorMenu: FunctionComponent<ChangeEditorMenuProps> = ({
|
||||
const selectComponent = useCallback(
|
||||
async (uiFeature: UIFeature<EditorFeatureDescription | IframeComponentFeatureDescription>, note: SNNote) => {
|
||||
if (uiFeature.isComponent && uiFeature.asComponent.conflictOf) {
|
||||
void application.changeAndSaveItem(uiFeature.asComponent, (mutator) => {
|
||||
void application.changeAndSaveItem.execute(uiFeature.asComponent, (mutator) => {
|
||||
mutator.conflictOf = undefined
|
||||
})
|
||||
}
|
||||
|
||||
await application.controllers.itemListController.insertCurrentIfTemplate()
|
||||
await application.itemListController.insertCurrentIfTemplate()
|
||||
|
||||
await application.changeAndSaveItem(note, (mutator) => {
|
||||
await application.changeAndSaveItem.execute(note, (mutator) => {
|
||||
const noteMutator = mutator as NoteMutator
|
||||
noteMutator.noteType = uiFeature.noteType
|
||||
noteMutator.editorIdentifier = uiFeature.featureIdentifier
|
||||
|
||||
@@ -42,12 +42,12 @@ const ChangeEditorMultipleMenu = ({ application, notes, setDisableClickOutside }
|
||||
const selectComponent = useCallback(
|
||||
async (uiFeature: UIFeature<EditorFeatureDescription | IframeComponentFeatureDescription>, note: SNNote) => {
|
||||
if (uiFeature.isComponent && uiFeature.asComponent.conflictOf) {
|
||||
void application.changeAndSaveItem(uiFeature.asComponent, (mutator) => {
|
||||
void application.changeAndSaveItem.execute(uiFeature.asComponent, (mutator) => {
|
||||
mutator.conflictOf = undefined
|
||||
})
|
||||
}
|
||||
|
||||
await application.changeAndSaveItem(note, (mutator) => {
|
||||
await application.changeAndSaveItem.execute(note, (mutator) => {
|
||||
const noteMutator = mutator as NoteMutator
|
||||
noteMutator.noteType = uiFeature.noteType
|
||||
noteMutator.editorIdentifier = uiFeature.featureIdentifier
|
||||
|
||||
@@ -24,7 +24,17 @@ const ClippedNoteView = ({
|
||||
}) => {
|
||||
const application = useApplication()
|
||||
|
||||
const syncController = useRef(new NoteSyncController(application, note))
|
||||
const syncController = useRef(
|
||||
new NoteSyncController(
|
||||
note,
|
||||
application.items,
|
||||
application.mutator,
|
||||
application.sessions,
|
||||
application.sync,
|
||||
application.alerts,
|
||||
application.isNativeMobileWebUseCase,
|
||||
),
|
||||
)
|
||||
useEffect(() => {
|
||||
const currentController = syncController.current
|
||||
return () => {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { WebApplicationGroup } from '@/Application/WebApplicationGroup'
|
||||
import { ViewControllerManager } from '@/Controllers/ViewControllerManager'
|
||||
import { useCallback, useEffect, useState } from 'react'
|
||||
import { AccountMenuPane } from '../AccountMenu/AccountMenuPane'
|
||||
import MenuPaneSelector from '../AccountMenu/MenuPaneSelector'
|
||||
@@ -29,7 +28,7 @@ import { getSuperJSONFromClipPayload } from './getSuperJSONFromClipHTML'
|
||||
import ClippedNoteView from './ClippedNoteView'
|
||||
import { PremiumFeatureIconClass, PremiumFeatureIconName } from '../Icon/PremiumFeatureIcon'
|
||||
import Button from '../Button/Button'
|
||||
import { openSubscriptionDashboard } from '@/Utils/ManageSubscription'
|
||||
|
||||
import { useStateRef } from '@/Hooks/useStateRef'
|
||||
import usePreference from '@/Hooks/usePreference'
|
||||
import { createLinkFromItem } from '@/Utils/Items/Search/createLinkFromItem'
|
||||
@@ -39,13 +38,7 @@ import StyledTooltip from '../StyledTooltip/StyledTooltip'
|
||||
import MenuSwitchButtonItem from '../Menu/MenuSwitchButtonItem'
|
||||
import Spinner from '../Spinner/Spinner'
|
||||
|
||||
const ClipperView = ({
|
||||
viewControllerManager,
|
||||
applicationGroup,
|
||||
}: {
|
||||
viewControllerManager: ViewControllerManager
|
||||
applicationGroup: WebApplicationGroup
|
||||
}) => {
|
||||
const ClipperView = ({ applicationGroup }: { applicationGroup: WebApplicationGroup }) => {
|
||||
const application = useApplication()
|
||||
|
||||
const [currentWindow, setCurrentWindow] = useState<Awaited<ReturnType<typeof windows.getCurrent>>>()
|
||||
@@ -61,7 +54,7 @@ const ClipperView = ({
|
||||
}, [])
|
||||
const isFirefoxPopup = !!currentWindow && currentWindow.type === 'popup' && currentWindow.incognito === false
|
||||
|
||||
const [user, setUser] = useState(() => application.getUser())
|
||||
const [user, setUser] = useState(() => application.sessions.getUser())
|
||||
const [isSyncing, setIsSyncing] = useState(false)
|
||||
const [hasSyncError, setHasSyncError] = useState(false)
|
||||
useEffect(() => {
|
||||
@@ -81,7 +74,7 @@ const ClipperView = ({
|
||||
case ApplicationEvent.SignedIn:
|
||||
case ApplicationEvent.SignedOut:
|
||||
case ApplicationEvent.UserRolesChanged:
|
||||
setUser(application.getUser())
|
||||
setUser(application.sessions.getUser())
|
||||
setIsEntitled(
|
||||
application.features.getFeatureStatus(
|
||||
NativeFeatureIdentifier.create(NativeFeatureIdentifier.TYPES.Clipper).getValue(),
|
||||
@@ -223,7 +216,7 @@ const ClipperView = ({
|
||||
type: 'image/png',
|
||||
})
|
||||
|
||||
const uploadedFile = await viewControllerManager.filesController.uploadNewFile(file).catch(console.error)
|
||||
const uploadedFile = await application.filesController.uploadNewFile(file).catch(console.error)
|
||||
|
||||
if (uploadedFile && defaultTagRef.current) {
|
||||
await application.linkingController.linkItems(uploadedFile, defaultTagRef.current)
|
||||
@@ -269,6 +262,7 @@ const ClipperView = ({
|
||||
|
||||
createNoteFromClip().catch(console.error)
|
||||
}, [
|
||||
application.filesController,
|
||||
application.items,
|
||||
application.linkingController,
|
||||
application.mutator,
|
||||
@@ -276,12 +270,11 @@ const ClipperView = ({
|
||||
clipPayload,
|
||||
defaultTagRef,
|
||||
isEntitledRef,
|
||||
viewControllerManager.filesController,
|
||||
])
|
||||
|
||||
const upgradePlan = useCallback(async () => {
|
||||
if (hasSubscription) {
|
||||
await openSubscriptionDashboard(application)
|
||||
await application.openSubscriptionDashboard.execute()
|
||||
} else {
|
||||
await application.openPurchaseFlow()
|
||||
}
|
||||
@@ -317,7 +310,7 @@ const ClipperView = ({
|
||||
<ClippedNoteView
|
||||
note={clippedNote}
|
||||
key={clippedNote.uuid}
|
||||
linkingController={viewControllerManager.linkingController}
|
||||
linkingController={application.linkingController}
|
||||
clearClip={clearClip}
|
||||
isFirefoxPopup={isFirefoxPopup}
|
||||
/>
|
||||
@@ -328,8 +321,6 @@ const ClipperView = ({
|
||||
return menuPane ? (
|
||||
<div className="py-1">
|
||||
<MenuPaneSelector
|
||||
viewControllerManager={viewControllerManager}
|
||||
application={application}
|
||||
mainApplicationGroup={applicationGroup}
|
||||
menuPane={menuPane}
|
||||
setMenuPane={setMenuPane}
|
||||
|
||||
@@ -153,7 +153,7 @@ const IframeFeatureView: FunctionComponent<Props> = ({ onLoad, componentViewer,
|
||||
application.keyboardService.handleComponentKeyUp(data.keyboardModifier)
|
||||
break
|
||||
case ComponentAction.Click:
|
||||
application.controllers.notesController.setContextMenuOpen(false)
|
||||
application.notesController.setContextMenuOpen(false)
|
||||
break
|
||||
default:
|
||||
return
|
||||
@@ -165,13 +165,13 @@ const IframeFeatureView: FunctionComponent<Props> = ({ onLoad, componentViewer,
|
||||
}, [componentViewer, application])
|
||||
|
||||
useEffect(() => {
|
||||
const unregisterDesktopObserver = application
|
||||
.getDesktopService()
|
||||
?.registerUpdateObserver((updatedComponent: ComponentInterface) => {
|
||||
const unregisterDesktopObserver = application.desktopManager?.registerUpdateObserver(
|
||||
(updatedComponent: ComponentInterface) => {
|
||||
if (updatedComponent.uuid === uiFeature.uniqueIdentifier.value) {
|
||||
requestReload?.(componentViewer)
|
||||
}
|
||||
})
|
||||
},
|
||||
)
|
||||
|
||||
return () => {
|
||||
unregisterDesktopObserver?.()
|
||||
|
||||
@@ -3,7 +3,6 @@ import { FunctionComponent, useCallback } from 'react'
|
||||
import Button from '@/Components/Button/Button'
|
||||
import { WarningCircle } from '../UIElements/WarningCircle'
|
||||
import { useApplication } from '../ApplicationProvider'
|
||||
import { openSubscriptionDashboard } from '@/Utils/ManageSubscription'
|
||||
|
||||
type Props = {
|
||||
feature: AnyFeatureDescription
|
||||
@@ -33,7 +32,7 @@ const NotEntitledBanner: FunctionComponent<Props> = ({ featureStatus, feature })
|
||||
const expiredDate = application.subscriptions.userSubscriptionExpirationDate
|
||||
|
||||
const manageSubscription = useCallback(() => {
|
||||
void openSubscriptionDashboard(application)
|
||||
void application.openSubscriptionDashboard.execute()
|
||||
}, [application])
|
||||
|
||||
return (
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import { ViewControllerManager } from '@Controllers/ViewControllerManager'
|
||||
import { useCallback, useRef } from 'react'
|
||||
import { STRING_DELETE_ACCOUNT_CONFIRMATION } from '@/Constants/Strings'
|
||||
import Button from '@/Components/Button/Button'
|
||||
@@ -8,14 +7,13 @@ import Icon from '../Icon/Icon'
|
||||
import AlertDialog from '../AlertDialog/AlertDialog'
|
||||
|
||||
type Props = {
|
||||
viewControllerManager: ViewControllerManager
|
||||
application: WebApplication
|
||||
}
|
||||
|
||||
const ConfirmDeleteAccountModal = ({ application, viewControllerManager }: Props) => {
|
||||
const ConfirmDeleteAccountModal = ({ application }: Props) => {
|
||||
const closeDialog = useCallback(() => {
|
||||
viewControllerManager.accountMenuController.setDeletingAccount(false)
|
||||
}, [viewControllerManager.accountMenuController])
|
||||
application.accountMenuController.setDeletingAccount(false)
|
||||
}, [application.accountMenuController])
|
||||
|
||||
const cancelRef = useRef<HTMLButtonElement>(null)
|
||||
|
||||
@@ -52,7 +50,7 @@ const ConfirmDeleteAccountModal = ({ application, viewControllerManager }: Props
|
||||
ConfirmDeleteAccountModal.displayName = 'ConfirmDeleteAccountModal'
|
||||
|
||||
const ConfirmDeleteAccountContainer = (props: Props) => {
|
||||
if (!props.viewControllerManager.accountMenuController.deletingAccount) {
|
||||
if (!props.application.accountMenuController.deletingAccount) {
|
||||
return null
|
||||
}
|
||||
return <ConfirmDeleteAccountModal {...props} />
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { FunctionComponent, useCallback, useRef } from 'react'
|
||||
import { STRING_SIGN_OUT_CONFIRMATION } from '@/Constants/Strings'
|
||||
import { WebApplication } from '@/Application/WebApplication'
|
||||
import { ViewControllerManager } from '@/Controllers/ViewControllerManager'
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import { WebApplicationGroup } from '@/Application/WebApplicationGroup'
|
||||
import { isDesktopApplication } from '@/Utils'
|
||||
@@ -12,11 +11,10 @@ import HorizontalSeparator from '../Shared/HorizontalSeparator'
|
||||
|
||||
type Props = {
|
||||
application: WebApplication
|
||||
viewControllerManager: ViewControllerManager
|
||||
applicationGroup: WebApplicationGroup
|
||||
}
|
||||
|
||||
const ConfirmSignoutModal: FunctionComponent<Props> = ({ application, viewControllerManager, applicationGroup }) => {
|
||||
const ConfirmSignoutModal: FunctionComponent<Props> = ({ application, applicationGroup }) => {
|
||||
const hasAnyBackupsEnabled =
|
||||
application.fileBackups?.isFilesBackupsEnabled() ||
|
||||
application.fileBackups?.isPlaintextBackupsEnabled() ||
|
||||
@@ -24,8 +22,8 @@ const ConfirmSignoutModal: FunctionComponent<Props> = ({ application, viewContro
|
||||
|
||||
const cancelRef = useRef<HTMLButtonElement>(null)
|
||||
const closeDialog = useCallback(() => {
|
||||
viewControllerManager.accountMenuController.setSigningOut(false)
|
||||
}, [viewControllerManager.accountMenuController])
|
||||
application.accountMenuController.setSigningOut(false)
|
||||
}, [application.accountMenuController])
|
||||
|
||||
const workspaces = applicationGroup.getDescriptors()
|
||||
const showWorkspaceWarning = workspaces.length > 1 && isDesktopApplication()
|
||||
@@ -98,7 +96,7 @@ const ConfirmSignoutModal: FunctionComponent<Props> = ({ application, viewContro
|
||||
ConfirmSignoutModal.displayName = 'ConfirmSignoutModal'
|
||||
|
||||
const ConfirmSignoutContainer = (props: Props) => {
|
||||
if (!props.viewControllerManager.accountMenuController.signingOut) {
|
||||
if (!props.application.accountMenuController.signingOut) {
|
||||
return null
|
||||
}
|
||||
return <ConfirmSignoutModal {...props} />
|
||||
|
||||
@@ -5,39 +5,22 @@ import { FunctionComponent, KeyboardEventHandler, UIEventHandler, useCallback }
|
||||
import { FOCUSABLE_BUT_NOT_TABBABLE, NOTES_LIST_SCROLL_THRESHOLD } from '@/Constants/Constants'
|
||||
import { ListableContentItem } from './Types/ListableContentItem'
|
||||
import ContentListItem from './ContentListItem'
|
||||
import { ItemListController } from '@/Controllers/ItemList/ItemListController'
|
||||
import { FilesController } from '@/Controllers/FilesController'
|
||||
import { SelectedItemsController } from '@/Controllers/SelectedItemsController'
|
||||
import { NavigationController } from '@/Controllers/Navigation/NavigationController'
|
||||
import { NotesController } from '@/Controllers/NotesController/NotesController'
|
||||
import { ElementIds } from '@/Constants/ElementIDs'
|
||||
import { classNames } from '@standardnotes/utils'
|
||||
import { ContentType, SNTag } from '@standardnotes/snjs'
|
||||
import { ItemListController } from '@/Controllers/ItemList/ItemListController'
|
||||
|
||||
type Props = {
|
||||
application: WebApplication
|
||||
filesController: FilesController
|
||||
itemListController: ItemListController
|
||||
items: ListableContentItem[]
|
||||
navigationController: NavigationController
|
||||
notesController: NotesController
|
||||
selectionController: SelectedItemsController
|
||||
selectedUuids: SelectedItemsController['selectedUuids']
|
||||
selectedUuids: ItemListController['selectedUuids']
|
||||
paginate: () => void
|
||||
}
|
||||
|
||||
const ContentList: FunctionComponent<Props> = ({
|
||||
application,
|
||||
filesController,
|
||||
itemListController,
|
||||
items,
|
||||
navigationController,
|
||||
notesController,
|
||||
selectionController,
|
||||
selectedUuids,
|
||||
paginate,
|
||||
}) => {
|
||||
const { selectPreviousItem, selectNextItem } = selectionController
|
||||
const ContentList: FunctionComponent<Props> = ({ application, items, selectedUuids, paginate }) => {
|
||||
const { filesController, itemListController, navigationController, notesController } = application
|
||||
|
||||
const { selectPreviousItem, selectNextItem } = itemListController
|
||||
const { hideTags, hideDate, hideNotePreview, hideEditorIcon } = itemListController.webDisplayOptions
|
||||
const { sortBy } = itemListController.displayOptions
|
||||
const selectedTag = navigationController.selected
|
||||
@@ -68,9 +51,9 @@ const ContentList: FunctionComponent<Props> = ({
|
||||
|
||||
const selectItem = useCallback(
|
||||
(item: ListableContentItem, userTriggered?: boolean) => {
|
||||
return selectionController.selectItem(item.uuid, userTriggered)
|
||||
return itemListController.selectItem(item.uuid, userTriggered)
|
||||
},
|
||||
[selectionController],
|
||||
[itemListController],
|
||||
)
|
||||
|
||||
const getTagsForItem = useCallback(
|
||||
|
||||
@@ -14,22 +14,13 @@ import { observer } from 'mobx-react-lite'
|
||||
import { forwardRef, useCallback, useEffect, useMemo } from 'react'
|
||||
import ContentList from '@/Components/ContentListView/ContentList'
|
||||
import NoAccountWarning from '@/Components/NoAccountWarning/NoAccountWarning'
|
||||
import { ItemListController } from '@/Controllers/ItemList/ItemListController'
|
||||
import { SelectedItemsController } from '@/Controllers/SelectedItemsController'
|
||||
import { NavigationController } from '@/Controllers/Navigation/NavigationController'
|
||||
import { FilesController } from '@/Controllers/FilesController'
|
||||
import { NoAccountWarningController } from '@/Controllers/NoAccountWarningController'
|
||||
import { NotesController } from '@/Controllers/NotesController/NotesController'
|
||||
import { AccountMenuController } from '@/Controllers/AccountMenu/AccountMenuController'
|
||||
import { ElementIds } from '@/Constants/ElementIDs'
|
||||
import ContentListHeader from './Header/ContentListHeader'
|
||||
import { AppPaneId } from '../Panes/AppPaneMetadata'
|
||||
import { useResponsiveAppPane } from '../Panes/ResponsivePaneProvider'
|
||||
import SearchBar from '../SearchBar/SearchBar'
|
||||
import { SearchOptionsController } from '@/Controllers/SearchOptionsController'
|
||||
import { classNames } from '@standardnotes/utils'
|
||||
import { useFileDragNDrop } from '../FileDragNDropProvider'
|
||||
import { LinkingController } from '@/Controllers/LinkingController'
|
||||
import DailyContentList from './Daily/DailyContentList'
|
||||
import { ListableContentItem } from './Types/ListableContentItem'
|
||||
import { FeatureName } from '@/Controllers/FeatureName'
|
||||
@@ -37,29 +28,14 @@ import { PanelResizedData } from '@/Types/PanelResizedData'
|
||||
import { useForwardedRef } from '@/Hooks/useForwardedRef'
|
||||
import FloatingAddButton from './FloatingAddButton'
|
||||
import ContentTableView from '../ContentTableView/ContentTableView'
|
||||
import { FeaturesController } from '@/Controllers/FeaturesController'
|
||||
import { MutuallyExclusiveMediaQueryBreakpoints, useMediaQuery } from '@/Hooks/useMediaQuery'
|
||||
import { HistoryModalController } from '@/Controllers/NoteHistory/HistoryModalController'
|
||||
import { PaneController } from '@/Controllers/PaneController/PaneController'
|
||||
import EmptyFilesView from './EmptyFilesView'
|
||||
import { PaneLayout } from '@/Controllers/PaneController/PaneLayout'
|
||||
import { usePaneSwipeGesture } from '../Panes/usePaneGesture'
|
||||
import { mergeRefs } from '@/Hooks/mergeRefs'
|
||||
|
||||
type Props = {
|
||||
accountMenuController: AccountMenuController
|
||||
application: WebApplication
|
||||
filesController: FilesController
|
||||
itemListController: ItemListController
|
||||
navigationController: NavigationController
|
||||
noAccountWarningController: NoAccountWarningController
|
||||
notesController: NotesController
|
||||
selectionController: SelectedItemsController
|
||||
searchOptionsController: SearchOptionsController
|
||||
linkingController: LinkingController
|
||||
featuresController: FeaturesController
|
||||
historyModalController: HistoryModalController
|
||||
paneController: PaneController
|
||||
className?: string
|
||||
id: string
|
||||
children?: React.ReactNode
|
||||
@@ -67,30 +43,20 @@ type Props = {
|
||||
}
|
||||
|
||||
const ContentListView = forwardRef<HTMLDivElement, Props>(
|
||||
(
|
||||
{
|
||||
({ application, className, id, children, onPanelWidthLoad }, ref) => {
|
||||
const {
|
||||
paneController,
|
||||
accountMenuController,
|
||||
application,
|
||||
filesController,
|
||||
itemListController,
|
||||
navigationController,
|
||||
noAccountWarningController,
|
||||
notesController,
|
||||
selectionController,
|
||||
searchOptionsController,
|
||||
linkingController,
|
||||
featuresController,
|
||||
historyModalController,
|
||||
paneController,
|
||||
className,
|
||||
id,
|
||||
children,
|
||||
onPanelWidthLoad,
|
||||
},
|
||||
ref,
|
||||
) => {
|
||||
} = application
|
||||
|
||||
const { setPaneLayout, panes } = useResponsiveAppPane()
|
||||
const { selectedUuids, selectNextItem, selectPreviousItem } = selectionController
|
||||
|
||||
const { selected: selectedTag, selectedAsTag } = navigationController
|
||||
const {
|
||||
completedFullSync,
|
||||
@@ -102,6 +68,9 @@ const ContentListView = forwardRef<HTMLDivElement, Props>(
|
||||
items,
|
||||
isCurrentNoteTemplate,
|
||||
isTableViewEnabled,
|
||||
selectedUuids,
|
||||
selectNextItem,
|
||||
selectPreviousItem,
|
||||
} = itemListController
|
||||
|
||||
const innerRef = useForwardedRef(ref)
|
||||
@@ -260,7 +229,7 @@ const ContentListView = forwardRef<HTMLDivElement, Props>(
|
||||
}
|
||||
|
||||
event.preventDefault()
|
||||
selectionController.selectAll()
|
||||
itemListController.selectAll()
|
||||
},
|
||||
},
|
||||
])
|
||||
@@ -268,9 +237,9 @@ const ContentListView = forwardRef<HTMLDivElement, Props>(
|
||||
addNewItem,
|
||||
application.keyboardService,
|
||||
createNewNote,
|
||||
itemListController,
|
||||
selectNextItem,
|
||||
selectPreviousItem,
|
||||
selectionController,
|
||||
shouldUseTableView,
|
||||
])
|
||||
|
||||
@@ -289,13 +258,13 @@ const ContentListView = forwardRef<HTMLDivElement, Props>(
|
||||
|
||||
const handleDailyListSelection = useCallback(
|
||||
async (item: ListableContentItem, userTriggered: boolean) => {
|
||||
await selectionController.selectItemWithScrollHandling(item, {
|
||||
await itemListController.selectItemWithScrollHandling(item, {
|
||||
userTriggered: true,
|
||||
scrollIntoView: userTriggered === false,
|
||||
animated: false,
|
||||
})
|
||||
},
|
||||
[selectionController],
|
||||
[itemListController],
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
@@ -371,29 +340,13 @@ const ContentListView = forwardRef<HTMLDivElement, Props>(
|
||||
) : null}
|
||||
{!dailyMode && renderedItems.length ? (
|
||||
shouldUseTableView ? (
|
||||
<ContentTableView
|
||||
items={items}
|
||||
application={application}
|
||||
filesController={filesController}
|
||||
featuresController={featuresController}
|
||||
linkingController={linkingController}
|
||||
navigationController={navigationController}
|
||||
notesController={notesController}
|
||||
historyModalController={historyModalController}
|
||||
itemListController={itemListController}
|
||||
selectionController={selectionController}
|
||||
/>
|
||||
<ContentTableView items={items} application={application} />
|
||||
) : (
|
||||
<ContentList
|
||||
items={renderedItems}
|
||||
selectedUuids={selectedUuids}
|
||||
application={application}
|
||||
paginate={paginate}
|
||||
filesController={filesController}
|
||||
itemListController={itemListController}
|
||||
navigationController={navigationController}
|
||||
notesController={notesController}
|
||||
selectionController={selectionController}
|
||||
/>
|
||||
)
|
||||
) : null}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { FunctionComponent, useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react'
|
||||
import { ListableContentItem } from '../Types/ListableContentItem'
|
||||
import { ItemListController } from '@/Controllers/ItemList/ItemListController'
|
||||
import { SelectedItemsController } from '@/Controllers/SelectedItemsController'
|
||||
import { useResponsiveAppPane } from '../../Panes/ResponsivePaneProvider'
|
||||
import { AppPaneId } from '../../Panes/AppPaneMetadata'
|
||||
import {
|
||||
@@ -25,7 +24,7 @@ type Props = {
|
||||
items: ListableContentItem[]
|
||||
onSelect: (item: ListableContentItem, userTriggered: boolean) => Promise<void>
|
||||
selectedTag: SNTag
|
||||
selectedUuids: SelectedItemsController['selectedUuids']
|
||||
selectedUuids: ItemListController['selectedUuids']
|
||||
}
|
||||
|
||||
const PageSize = 10
|
||||
|
||||
@@ -93,7 +93,7 @@ const DisplayOptionsMenu: FunctionComponent<DisplayOptionsMenuProps> = ({
|
||||
: selectedTag.preferences
|
||||
const [currentMode, setCurrentMode] = useState<PreferenceMode>(selectedTagPreferences ? 'tag' : 'global')
|
||||
const [preferences, setPreferences] = useState<TagPreferences>({})
|
||||
const hasSubscription = application.controllers.subscriptionController.hasFirstPartyOnlineOrOfflineSubscription
|
||||
const hasSubscription = application.subscriptionController.hasFirstPartyOnlineOrOfflineSubscription
|
||||
const controlsDisabled = currentMode === 'tag' && !hasSubscription
|
||||
const isDailyEntry = selectedTagPreferences?.entryMode === 'daily'
|
||||
|
||||
@@ -181,7 +181,7 @@ const DisplayOptionsMenu: FunctionComponent<DisplayOptionsMenuProps> = ({
|
||||
} else if (isSystemTag) {
|
||||
await changeSystemViewPreferences(properties)
|
||||
} else {
|
||||
await application.changeAndSaveItem<TagMutator>(selectedTag, (mutator) => {
|
||||
await application.changeAndSaveItem.execute<TagMutator>(selectedTag, (mutator) => {
|
||||
mutator.preferences = {
|
||||
...mutator.preferences,
|
||||
...properties,
|
||||
@@ -202,7 +202,7 @@ const DisplayOptionsMenu: FunctionComponent<DisplayOptionsMenuProps> = ({
|
||||
return
|
||||
}
|
||||
|
||||
void application.changeAndSaveItem<TagMutator>(selectedTag, (mutator) => {
|
||||
void application.changeAndSaveItem.execute<TagMutator>(selectedTag, (mutator) => {
|
||||
mutator.preferences = undefined
|
||||
})
|
||||
}, [application, isSystemTag, reloadPreferences, selectedTag])
|
||||
|
||||
@@ -231,7 +231,7 @@ const NewNotePreferences: FunctionComponent<Props> = ({
|
||||
onClick={(event) => {
|
||||
if (application.isNativeMobileWeb()) {
|
||||
event.preventDefault()
|
||||
application.mobileDevice().openUrl(HelpPageUrl)
|
||||
application.mobileDevice.openUrl(HelpPageUrl)
|
||||
}
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { WebApplication } from '@/Application/WebApplication'
|
||||
import { FilesController } from '@/Controllers/FilesController'
|
||||
import { formatDateForContextMenu } from '@/Utils/DateUtils'
|
||||
import { getIconForFileType } from '@/Utils/Items/Icons/getIconForFileType'
|
||||
import { formatSizeToReadableString } from '@standardnotes/filepicker'
|
||||
@@ -28,38 +27,15 @@ import FileMenuOptions from '../FileContextMenu/FileMenuOptions'
|
||||
import Icon from '../Icon/Icon'
|
||||
import LinkedItemBubble from '../LinkedItems/LinkedItemBubble'
|
||||
import LinkedItemsPanel from '../LinkedItems/LinkedItemsPanel'
|
||||
import { LinkingController } from '@/Controllers/LinkingController'
|
||||
import { FeaturesController } from '@/Controllers/FeaturesController'
|
||||
import { MutuallyExclusiveMediaQueryBreakpoints, useMediaQuery } from '@/Hooks/useMediaQuery'
|
||||
import { useApplication } from '../ApplicationProvider'
|
||||
import { NavigationController } from '@/Controllers/Navigation/NavigationController'
|
||||
import { getIconAndTintForNoteType } from '@/Utils/Items/Icons/getIconAndTintForNoteType'
|
||||
import NotesOptions from '../NotesOptions/NotesOptions'
|
||||
import { NotesController } from '@/Controllers/NotesController/NotesController'
|
||||
import { HistoryModalController } from '@/Controllers/NoteHistory/HistoryModalController'
|
||||
import { useItemLinks } from '@/Hooks/useItemLinks'
|
||||
import { ItemLink } from '@/Utils/Items/Search/ItemLink'
|
||||
import { ItemListController } from '@/Controllers/ItemList/ItemListController'
|
||||
import ListItemVaultInfo from '../ContentListView/ListItemVaultInfo'
|
||||
import { SelectedItemsController } from '@/Controllers/SelectedItemsController'
|
||||
|
||||
const ContextMenuCell = ({
|
||||
items,
|
||||
filesController,
|
||||
navigationController,
|
||||
linkingController,
|
||||
notesController,
|
||||
historyModalController,
|
||||
selectionController,
|
||||
}: {
|
||||
items: DecryptedItemInterface[]
|
||||
filesController: FilesController
|
||||
navigationController: NavigationController
|
||||
linkingController: LinkingController
|
||||
notesController: NotesController
|
||||
historyModalController: HistoryModalController
|
||||
selectionController: SelectedItemsController
|
||||
}) => {
|
||||
const ContextMenuCell = ({ items }: { items: DecryptedItemInterface[] }) => {
|
||||
const [contextMenuVisible, setContextMenuVisible] = useState(false)
|
||||
const anchorElementRef = useRef<HTMLButtonElement>(null)
|
||||
|
||||
@@ -102,12 +78,9 @@ const ContextMenuCell = ({
|
||||
<Menu a11yLabel="File context menu" isOpen={contextMenuVisible}>
|
||||
{allItemsAreFiles && (
|
||||
<FileMenuOptions
|
||||
linkingController={linkingController}
|
||||
navigationController={navigationController}
|
||||
closeMenu={() => {
|
||||
setContextMenuVisible(false)
|
||||
}}
|
||||
filesController={filesController}
|
||||
shouldShowRenameOption={false}
|
||||
shouldShowAttachOption={false}
|
||||
selectedFiles={items as FileItem[]}
|
||||
@@ -116,11 +89,6 @@ const ContextMenuCell = ({
|
||||
{allItemsAreNotes && (
|
||||
<NotesOptions
|
||||
notes={items as SNNote[]}
|
||||
navigationController={navigationController}
|
||||
notesController={notesController}
|
||||
linkingController={linkingController}
|
||||
historyModalController={historyModalController}
|
||||
selectionController={selectionController}
|
||||
closeMenu={() => {
|
||||
setContextMenuVisible(false)
|
||||
}}
|
||||
@@ -132,17 +100,7 @@ const ContextMenuCell = ({
|
||||
)
|
||||
}
|
||||
|
||||
const ItemLinksCell = ({
|
||||
item,
|
||||
filesController,
|
||||
linkingController,
|
||||
featuresController,
|
||||
}: {
|
||||
item: DecryptedItemInterface
|
||||
filesController: FilesController
|
||||
linkingController: LinkingController
|
||||
featuresController: FeaturesController
|
||||
}) => {
|
||||
const ItemLinksCell = ({ item }: { item: DecryptedItemInterface }) => {
|
||||
const [contextMenuVisible, setContextMenuVisible] = useState(false)
|
||||
const anchorElementRef = useRef<HTMLButtonElement>(null)
|
||||
|
||||
@@ -170,13 +128,7 @@ const ItemLinksCell = ({
|
||||
align="start"
|
||||
className="py-2"
|
||||
>
|
||||
<LinkedItemsPanel
|
||||
linkingController={linkingController}
|
||||
filesController={filesController}
|
||||
featuresController={featuresController}
|
||||
isOpen={contextMenuVisible}
|
||||
item={item}
|
||||
/>
|
||||
<LinkedItemsPanel isOpen={contextMenuVisible} item={item} />
|
||||
</Popover>
|
||||
</>
|
||||
)
|
||||
@@ -263,37 +215,18 @@ const AttachedToCell = ({ item }: { item: DecryptedItemInterface }) => {
|
||||
type Props = {
|
||||
application: WebApplication
|
||||
items: DecryptedItemInterface[]
|
||||
filesController: FilesController
|
||||
featuresController: FeaturesController
|
||||
linkingController: LinkingController
|
||||
navigationController: NavigationController
|
||||
notesController: NotesController
|
||||
historyModalController: HistoryModalController
|
||||
itemListController: ItemListController
|
||||
selectionController: SelectedItemsController
|
||||
}
|
||||
|
||||
const ContentTableView = ({
|
||||
application,
|
||||
items,
|
||||
filesController,
|
||||
featuresController,
|
||||
linkingController,
|
||||
navigationController,
|
||||
notesController,
|
||||
historyModalController,
|
||||
itemListController,
|
||||
selectionController,
|
||||
}: Props) => {
|
||||
const ContentTableView = ({ application, items }: Props) => {
|
||||
const listHasFiles = items.some((item) => item instanceof FileItem)
|
||||
|
||||
const { sortBy, sortDirection } = itemListController.displayOptions
|
||||
const { sortBy, sortDirection } = application.itemListController.displayOptions
|
||||
const sortReversed = sortDirection === 'asc'
|
||||
const { hideDate, hideEditorIcon: hideIcon, hideTags } = itemListController.webDisplayOptions
|
||||
const { hideDate, hideEditorIcon: hideIcon, hideTags } = application.itemListController.webDisplayOptions
|
||||
|
||||
const onSortChange = useCallback(
|
||||
async (sortBy: keyof SortableItem, sortReversed: boolean) => {
|
||||
const selectedTag = navigationController.selected
|
||||
const selectedTag = application.navigationController.selected
|
||||
|
||||
if (!selectedTag) {
|
||||
return
|
||||
@@ -320,7 +253,7 @@ const ContentTableView = ({
|
||||
return
|
||||
}
|
||||
|
||||
await application.changeAndSaveItem<TagMutator>(selectedTag, (mutator) => {
|
||||
await application.changeAndSaveItem.execute<TagMutator>(selectedTag, (mutator) => {
|
||||
mutator.preferences = {
|
||||
...mutator.preferences,
|
||||
sortBy,
|
||||
@@ -328,7 +261,7 @@ const ContentTableView = ({
|
||||
}
|
||||
})
|
||||
},
|
||||
[application, navigationController.selected],
|
||||
[application],
|
||||
)
|
||||
|
||||
const [contextMenuItem, setContextMenuItem] = useState<DecryptedItemInterface | undefined>(undefined)
|
||||
@@ -383,7 +316,7 @@ const ContentTableView = ({
|
||||
enableMultipleRowSelection: true,
|
||||
onRowActivate(item) {
|
||||
if (item instanceof FileItem) {
|
||||
void filesController.handleFileAction({
|
||||
void application.filesController.handleFileAction({
|
||||
type: FileItemActionType.PreviewFile,
|
||||
payload: {
|
||||
file: item,
|
||||
@@ -399,35 +332,12 @@ const ContentTableView = ({
|
||||
rowActions: (item) => {
|
||||
return (
|
||||
<div className="flex items-center gap-2">
|
||||
<ItemLinksCell
|
||||
item={item}
|
||||
filesController={filesController}
|
||||
featuresController={featuresController}
|
||||
linkingController={linkingController}
|
||||
/>
|
||||
<ContextMenuCell
|
||||
items={[item]}
|
||||
filesController={filesController}
|
||||
linkingController={linkingController}
|
||||
navigationController={navigationController}
|
||||
notesController={notesController}
|
||||
historyModalController={historyModalController}
|
||||
selectionController={selectionController}
|
||||
/>
|
||||
<ItemLinksCell item={item} />
|
||||
<ContextMenuCell items={[item]} />
|
||||
</div>
|
||||
)
|
||||
},
|
||||
selectionActions: (itemIds) => (
|
||||
<ContextMenuCell
|
||||
items={items.filter((item) => itemIds.includes(item.uuid))}
|
||||
filesController={filesController}
|
||||
linkingController={linkingController}
|
||||
navigationController={navigationController}
|
||||
notesController={notesController}
|
||||
historyModalController={historyModalController}
|
||||
selectionController={selectionController}
|
||||
/>
|
||||
),
|
||||
selectionActions: (itemIds) => <ContextMenuCell items={items.filter((item) => itemIds.includes(item.uuid))} />,
|
||||
showSelectionActions: true,
|
||||
})
|
||||
|
||||
@@ -456,26 +366,15 @@ const ContentTableView = ({
|
||||
<Menu a11yLabel="File context menu" isOpen={true}>
|
||||
<FileMenuOptions
|
||||
closeMenu={closeContextMenu}
|
||||
filesController={filesController}
|
||||
shouldShowRenameOption={false}
|
||||
shouldShowAttachOption={false}
|
||||
selectedFiles={[contextMenuItem]}
|
||||
linkingController={linkingController}
|
||||
navigationController={navigationController}
|
||||
/>
|
||||
</Menu>
|
||||
)}
|
||||
{contextMenuItem instanceof SNNote && (
|
||||
<Menu className="select-none" a11yLabel="Note context menu" isOpen={true}>
|
||||
<NotesOptions
|
||||
notes={[contextMenuItem]}
|
||||
navigationController={navigationController}
|
||||
notesController={notesController}
|
||||
linkingController={linkingController}
|
||||
historyModalController={historyModalController}
|
||||
selectionController={selectionController}
|
||||
closeMenu={closeContextMenu}
|
||||
/>
|
||||
<NotesOptions notes={[contextMenuItem]} closeMenu={closeContextMenu} />
|
||||
</Menu>
|
||||
)}
|
||||
</Popover>
|
||||
|
||||
@@ -154,7 +154,7 @@ const EditorWidthSelectionModal = ({
|
||||
|
||||
const EditorWidthSelectionModalWrapper = () => {
|
||||
const application = useApplication()
|
||||
const { notesController } = application.controllers
|
||||
const { notesController } = application
|
||||
|
||||
const [isOpen, setIsOpen] = useState(false)
|
||||
const [isGlobal, setIsGlobal] = useState(false)
|
||||
|
||||
@@ -1,60 +1,46 @@
|
||||
import { FilesController } from '@/Controllers/FilesController'
|
||||
import { LinkingController } from '@/Controllers/LinkingController'
|
||||
import { NavigationController } from '@/Controllers/Navigation/NavigationController'
|
||||
import { SelectedItemsController } from '@/Controllers/SelectedItemsController'
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import { FunctionComponent } from 'react'
|
||||
import Menu from '../Menu/Menu'
|
||||
import Popover from '../Popover/Popover'
|
||||
import FileMenuOptions from './FileMenuOptions'
|
||||
import { ItemListController } from '@/Controllers/ItemList/ItemListController'
|
||||
|
||||
type Props = {
|
||||
filesController: FilesController
|
||||
selectionController: SelectedItemsController
|
||||
linkingController: LinkingController
|
||||
navigationController: NavigationController
|
||||
itemListController: ItemListController
|
||||
}
|
||||
|
||||
const FileContextMenu: FunctionComponent<Props> = observer(
|
||||
({ filesController, selectionController, linkingController, navigationController }) => {
|
||||
const { showFileContextMenu, setShowFileContextMenu, fileContextMenuLocation } = filesController
|
||||
const { selectedFiles } = selectionController
|
||||
const FileContextMenu: FunctionComponent<Props> = observer(({ filesController, itemListController }) => {
|
||||
const { showFileContextMenu, setShowFileContextMenu, fileContextMenuLocation } = filesController
|
||||
const { selectedFiles } = itemListController
|
||||
|
||||
return (
|
||||
<Popover
|
||||
title="File options"
|
||||
open={showFileContextMenu}
|
||||
anchorPoint={fileContextMenuLocation}
|
||||
togglePopover={() => setShowFileContextMenu(!showFileContextMenu)}
|
||||
align="start"
|
||||
className="py-2"
|
||||
>
|
||||
<Menu a11yLabel="File context menu" isOpen={showFileContextMenu}>
|
||||
<FileMenuOptions
|
||||
filesController={filesController}
|
||||
linkingController={linkingController}
|
||||
navigationController={navigationController}
|
||||
selectedFiles={selectedFiles}
|
||||
closeMenu={() => setShowFileContextMenu(false)}
|
||||
shouldShowRenameOption={false}
|
||||
shouldShowAttachOption={false}
|
||||
/>
|
||||
</Menu>
|
||||
</Popover>
|
||||
)
|
||||
},
|
||||
)
|
||||
return (
|
||||
<Popover
|
||||
title="File options"
|
||||
open={showFileContextMenu}
|
||||
anchorPoint={fileContextMenuLocation}
|
||||
togglePopover={() => setShowFileContextMenu(!showFileContextMenu)}
|
||||
align="start"
|
||||
className="py-2"
|
||||
>
|
||||
<Menu a11yLabel="File context menu" isOpen={showFileContextMenu}>
|
||||
<FileMenuOptions
|
||||
selectedFiles={selectedFiles}
|
||||
closeMenu={() => setShowFileContextMenu(false)}
|
||||
shouldShowRenameOption={false}
|
||||
shouldShowAttachOption={false}
|
||||
/>
|
||||
</Menu>
|
||||
</Popover>
|
||||
)
|
||||
})
|
||||
|
||||
FileContextMenu.displayName = 'FileContextMenu'
|
||||
|
||||
const FileContextMenuWrapper: FunctionComponent<Props> = ({
|
||||
filesController,
|
||||
linkingController,
|
||||
navigationController,
|
||||
selectionController,
|
||||
}) => {
|
||||
const FileContextMenuWrapper: FunctionComponent<Props> = ({ filesController, itemListController }) => {
|
||||
const { showFileContextMenu } = filesController
|
||||
const { selectedFiles } = selectionController
|
||||
const { selectedFiles } = itemListController
|
||||
|
||||
const selectedFile = selectedFiles[0]
|
||||
|
||||
@@ -62,14 +48,7 @@ const FileContextMenuWrapper: FunctionComponent<Props> = ({
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<FileContextMenu
|
||||
filesController={filesController}
|
||||
linkingController={linkingController}
|
||||
navigationController={navigationController}
|
||||
selectionController={selectionController}
|
||||
/>
|
||||
)
|
||||
return <FileContextMenu filesController={filesController} itemListController={itemListController} />
|
||||
}
|
||||
|
||||
export default observer(FileContextMenuWrapper)
|
||||
|
||||
@@ -2,7 +2,6 @@ import { FunctionComponent, useCallback, useMemo } from 'react'
|
||||
import { FileItemActionType } from '../AttachedFilesPopover/PopoverFileItemAction'
|
||||
import Icon from '@/Components/Icon/Icon'
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import { FilesController } from '@/Controllers/FilesController'
|
||||
import HorizontalSeparator from '../Shared/HorizontalSeparator'
|
||||
import { formatSizeToReadableString } from '@standardnotes/filepicker'
|
||||
import { useResponsiveAppPane } from '../Panes/ResponsivePaneProvider'
|
||||
@@ -13,17 +12,13 @@ import MenuSwitchButtonItem from '../Menu/MenuSwitchButtonItem'
|
||||
import { FileItem } from '@standardnotes/snjs'
|
||||
import AddTagOption from '../NotesOptions/AddTagOption'
|
||||
import { MenuItemIconSize } from '@/Constants/TailwindClassNames'
|
||||
import { LinkingController } from '@/Controllers/LinkingController'
|
||||
import { NavigationController } from '@/Controllers/Navigation/NavigationController'
|
||||
import AddToVaultMenuOption from '../Vaults/AddToVaultMenuOption'
|
||||
import { iconClass } from '../NotesOptions/ClassNames'
|
||||
import { featureTrunkVaultsEnabled } from '@/FeatureTrunk'
|
||||
import { useApplication } from '../ApplicationProvider'
|
||||
|
||||
type Props = {
|
||||
closeMenu: () => void
|
||||
filesController: FilesController
|
||||
linkingController: LinkingController
|
||||
navigationController: NavigationController
|
||||
isFileAttachedToNote?: boolean
|
||||
renameToggleCallback?: (isRenamingFile: boolean) => void
|
||||
shouldShowRenameOption: boolean
|
||||
@@ -33,16 +28,15 @@ type Props = {
|
||||
|
||||
const FileMenuOptions: FunctionComponent<Props> = ({
|
||||
closeMenu,
|
||||
filesController,
|
||||
linkingController,
|
||||
navigationController,
|
||||
isFileAttachedToNote,
|
||||
renameToggleCallback,
|
||||
shouldShowRenameOption,
|
||||
shouldShowAttachOption,
|
||||
selectedFiles,
|
||||
}) => {
|
||||
const { handleFileAction } = filesController
|
||||
const application = useApplication()
|
||||
|
||||
const { handleFileAction } = application.filesController
|
||||
const { toggleAppPane } = useResponsiveAppPane()
|
||||
|
||||
const hasProtectedFiles = useMemo(() => selectedFiles.some((file) => file.protected), [selectedFiles])
|
||||
@@ -95,15 +89,15 @@ const FileMenuOptions: FunctionComponent<Props> = ({
|
||||
)}
|
||||
{featureTrunkVaultsEnabled() && <AddToVaultMenuOption iconClassName={iconClass} items={selectedFiles} />}
|
||||
<AddTagOption
|
||||
navigationController={navigationController}
|
||||
linkingController={linkingController}
|
||||
navigationController={application.navigationController}
|
||||
linkingController={application.linkingController}
|
||||
selectedItems={selectedFiles}
|
||||
iconClassName={`text-neutral mr-2 ${MenuItemIconSize}`}
|
||||
/>
|
||||
<MenuSwitchButtonItem
|
||||
checked={hasProtectedFiles}
|
||||
onChange={(hasProtectedFiles) => {
|
||||
void filesController.setProtectionForFiles(hasProtectedFiles, selectedFiles)
|
||||
void application.filesController.setProtectionForFiles(hasProtectedFiles, selectedFiles)
|
||||
}}
|
||||
>
|
||||
<Icon type="lock" className={`mr-2 text-neutral ${MenuItemIconSize}`} />
|
||||
@@ -112,7 +106,7 @@ const FileMenuOptions: FunctionComponent<Props> = ({
|
||||
<HorizontalSeparator classes="my-1" />
|
||||
<MenuItem
|
||||
onClick={() => {
|
||||
void filesController.downloadFiles(selectedFiles)
|
||||
void application.filesController.downloadFiles(selectedFiles)
|
||||
closeMenu()
|
||||
}}
|
||||
>
|
||||
@@ -132,7 +126,7 @@ const FileMenuOptions: FunctionComponent<Props> = ({
|
||||
<MenuItem
|
||||
onClick={() => {
|
||||
closeMenuAndToggleFilesList()
|
||||
void filesController.deleteFilesPermanently(selectedFiles)
|
||||
void application.filesController.deleteFilesPermanently(selectedFiles)
|
||||
}}
|
||||
>
|
||||
<Icon type="trash" className={`mr-2 text-danger ${MenuItemIconSize}`} />
|
||||
|
||||
@@ -1,27 +1,16 @@
|
||||
import { useCallback, useRef, useState } from 'react'
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import FileMenuOptions from './FileMenuOptions'
|
||||
import { FilesController } from '@/Controllers/FilesController'
|
||||
import { SelectedItemsController } from '@/Controllers/SelectedItemsController'
|
||||
import Popover from '../Popover/Popover'
|
||||
import RoundIconButton from '../Button/RoundIconButton'
|
||||
import Menu from '../Menu/Menu'
|
||||
import { LinkingController } from '@/Controllers/LinkingController'
|
||||
import { NavigationController } from '@/Controllers/Navigation/NavigationController'
|
||||
import { ItemListController } from '@/Controllers/ItemList/ItemListController'
|
||||
|
||||
type Props = {
|
||||
filesController: FilesController
|
||||
selectionController: SelectedItemsController
|
||||
linkingController: LinkingController
|
||||
navigationController: NavigationController
|
||||
itemListController: ItemListController
|
||||
}
|
||||
|
||||
const FilesOptionsPanel = ({
|
||||
filesController,
|
||||
linkingController,
|
||||
navigationController,
|
||||
selectionController,
|
||||
}: Props) => {
|
||||
const FilesOptionsPanel = ({ itemListController }: Props) => {
|
||||
const [isOpen, setIsOpen] = useState(false)
|
||||
const buttonRef = useRef<HTMLButtonElement>(null)
|
||||
|
||||
@@ -39,10 +28,7 @@ const FilesOptionsPanel = ({
|
||||
>
|
||||
<Menu a11yLabel="File options panel" isOpen={isOpen}>
|
||||
<FileMenuOptions
|
||||
filesController={filesController}
|
||||
linkingController={linkingController}
|
||||
navigationController={navigationController}
|
||||
selectedFiles={selectionController.selectedFiles}
|
||||
selectedFiles={itemListController.selectedFiles}
|
||||
closeMenu={() => {
|
||||
setIsOpen(false)
|
||||
}}
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
import { WebApplication } from '@/Application/WebApplication'
|
||||
import { FeaturesController } from '@/Controllers/FeaturesController'
|
||||
import { FilesController } from '@/Controllers/FilesController'
|
||||
import { usePremiumModal } from '@/Hooks/usePremiumModal'
|
||||
import { classNames } from '@standardnotes/utils'
|
||||
import { isHandlingFileDrag } from '@/Utils/DragTypeCheck'
|
||||
@@ -35,8 +33,6 @@ export const useFileDragNDrop = () => {
|
||||
|
||||
type Props = {
|
||||
application: WebApplication
|
||||
featuresController: FeaturesController
|
||||
filesController: FilesController
|
||||
children: ReactNode
|
||||
}
|
||||
|
||||
@@ -47,7 +43,7 @@ const MemoizedChildren = memo(({ children }: { children: ReactNode }) => {
|
||||
return <>{children}</>
|
||||
})
|
||||
|
||||
const FileDragNDropProvider = ({ application, children, featuresController, filesController }: Props) => {
|
||||
const FileDragNDropProvider = ({ application, children }: Props) => {
|
||||
const premiumModal = usePremiumModal()
|
||||
const [isDraggingFiles, setIsDraggingFiles] = useState(false)
|
||||
const [tooltipText, setTooltipText] = useState('')
|
||||
@@ -189,7 +185,7 @@ const FileDragNDropProvider = ({ application, children, featuresController, file
|
||||
|
||||
resetState()
|
||||
|
||||
if (!featuresController.entitledToFiles) {
|
||||
if (!application.featuresController.entitledToFiles) {
|
||||
premiumModal.activate('Files')
|
||||
return
|
||||
}
|
||||
@@ -204,7 +200,7 @@ const FileDragNDropProvider = ({ application, children, featuresController, file
|
||||
return
|
||||
}
|
||||
|
||||
const uploadedFile = await filesController.uploadNewFile(fileOrHandle)
|
||||
const uploadedFile = await application.filesController.uploadNewFile(fileOrHandle)
|
||||
|
||||
if (!uploadedFile) {
|
||||
return
|
||||
@@ -218,7 +214,7 @@ const FileDragNDropProvider = ({ application, children, featuresController, file
|
||||
dragCounter.current = 0
|
||||
}
|
||||
},
|
||||
[application, featuresController.entitledToFiles, filesController, premiumModal, resetState],
|
||||
[application, premiumModal, resetState],
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@@ -141,7 +141,7 @@ const FilePreview = ({ file, application, isEmbeddedInSuper = false, imageZoomLe
|
||||
) : (
|
||||
<FilePreviewError
|
||||
file={file}
|
||||
filesController={application.controllers.filesController}
|
||||
filesController={application.filesController}
|
||||
tryAgainCallback={() => {
|
||||
setDownloadedBytes(undefined)
|
||||
}}
|
||||
|
||||
@@ -5,7 +5,6 @@ import Icon from '@/Components/Icon/Icon'
|
||||
import FilePreviewInfoPanel from './FilePreviewInfoPanel'
|
||||
import { FOCUSABLE_BUT_NOT_TABBABLE } from '@/Constants/Constants'
|
||||
import { KeyboardKey } from '@standardnotes/ui-services'
|
||||
import { ViewControllerManager } from '@/Controllers/ViewControllerManager'
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import FilePreview from './FilePreview'
|
||||
import { getIconForFileType } from '@/Utils/Items/Icons/getIconForFileType'
|
||||
@@ -23,11 +22,10 @@ import { MutuallyExclusiveMediaQueryBreakpoints, useMediaQuery } from '@/Hooks/u
|
||||
|
||||
type Props = {
|
||||
application: WebApplication
|
||||
viewControllerManager: ViewControllerManager
|
||||
}
|
||||
|
||||
const FilePreviewModal = observer(({ application, viewControllerManager }: Props) => {
|
||||
const { currentFile, setCurrentFile, otherFiles, dismiss } = viewControllerManager.filePreviewModalController
|
||||
const FilePreviewModal = observer(({ application }: Props) => {
|
||||
const { currentFile, setCurrentFile, otherFiles, dismiss } = application.filePreviewModalController
|
||||
|
||||
const [isRenaming, setIsRenaming] = useState(false)
|
||||
const renameInputRef = useRef<HTMLInputElement>(null)
|
||||
@@ -229,9 +227,6 @@ const FilePreviewModal = observer(({ application, viewControllerManager }: Props
|
||||
>
|
||||
<Menu a11yLabel="File context menu" isOpen={showOptionsMenu}>
|
||||
<FileMenuOptions
|
||||
filesController={viewControllerManager.filesController}
|
||||
linkingController={viewControllerManager.linkingController}
|
||||
navigationController={viewControllerManager.navigationController}
|
||||
selectedFiles={[currentFile]}
|
||||
closeMenu={closeOptionsMenu}
|
||||
shouldShowRenameOption={false}
|
||||
@@ -260,10 +255,7 @@ const FilePreviewModal = observer(({ application, viewControllerManager }: Props
|
||||
</div>
|
||||
{showLinkedBubblesContainer && (
|
||||
<div className="-mt-1 min-h-0 flex-shrink-0 border-b border-border px-3.5 py-1.5">
|
||||
<LinkedItemBubblesContainer
|
||||
linkingController={viewControllerManager.linkingController}
|
||||
item={currentFile}
|
||||
/>
|
||||
<LinkedItemBubblesContainer linkingController={application.linkingController} item={currentFile} />
|
||||
</div>
|
||||
)}
|
||||
<div className="flex min-h-0 flex-grow flex-col-reverse md:flex-row">
|
||||
@@ -284,14 +276,14 @@ const FilePreviewModal = observer(({ application, viewControllerManager }: Props
|
||||
|
||||
FilePreviewModal.displayName = 'FilePreviewModal'
|
||||
|
||||
const FilePreviewModalWrapper: FunctionComponent<Props> = ({ application, viewControllerManager }) => {
|
||||
const FilePreviewModalWrapper: FunctionComponent<Props> = ({ application }) => {
|
||||
return (
|
||||
<ModalOverlay
|
||||
aria-label="File preview modal"
|
||||
isOpen={viewControllerManager.filePreviewModalController.isOpen}
|
||||
close={viewControllerManager.filePreviewModalController.dismiss}
|
||||
isOpen={application.filePreviewModalController.isOpen}
|
||||
close={application.filePreviewModalController.dismiss}
|
||||
>
|
||||
<FilePreviewModal application={application} viewControllerManager={viewControllerManager} />
|
||||
<FilePreviewModal application={application} />
|
||||
</ModalOverlay>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ const PreviewComponent: FunctionComponent<Props> = ({
|
||||
const sanitizedName = sanitizeFileName(name)
|
||||
const filename = `${sanitizedName}.${ext}`
|
||||
|
||||
void application.mobileDevice().previewFile(fileBase64, filename)
|
||||
void application.mobileDevice.previewFile(fileBase64, filename)
|
||||
}, [application, bytes, file.mimeType, file.name, isNativeMobileWeb])
|
||||
|
||||
if (isNativeMobileWeb && requiresNativePreview) {
|
||||
|
||||
@@ -5,16 +5,16 @@ import FileViewWithoutProtection from './FileViewWithoutProtection'
|
||||
import { FileViewProps } from './FileViewProps'
|
||||
import { ApplicationEvent } from '@standardnotes/snjs'
|
||||
|
||||
const FileView = ({ application, viewControllerManager, file }: FileViewProps) => {
|
||||
const FileView = ({ application, file }: FileViewProps) => {
|
||||
const [shouldShowProtectedOverlay, setShouldShowProtectedOverlay] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
viewControllerManager.filesController.setShowProtectedOverlay(!application.isAuthorizedToRenderItem(file))
|
||||
}, [application, file, viewControllerManager.filesController])
|
||||
application.filesController.setShowProtectedOverlay(!application.isAuthorizedToRenderItem(file))
|
||||
}, [application, file, application.filesController])
|
||||
|
||||
useEffect(() => {
|
||||
setShouldShowProtectedOverlay(viewControllerManager.filesController.showProtectedOverlay)
|
||||
}, [viewControllerManager.filesController.showProtectedOverlay])
|
||||
setShouldShowProtectedOverlay(application.filesController.showProtectedOverlay)
|
||||
}, [application.filesController.showProtectedOverlay])
|
||||
|
||||
const dismissProtectedOverlay = useCallback(async () => {
|
||||
let showFileContents = true
|
||||
@@ -48,7 +48,7 @@ const FileView = ({ application, viewControllerManager, file }: FileViewProps) =
|
||||
itemType={'file'}
|
||||
/>
|
||||
) : (
|
||||
<FileViewWithoutProtection application={application} viewControllerManager={viewControllerManager} file={file} />
|
||||
<FileViewWithoutProtection application={application} file={file} />
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
import { WebApplication } from '@/Application/WebApplication'
|
||||
import { ViewControllerManager } from '@/Controllers/ViewControllerManager'
|
||||
import { FileItem } from '@standardnotes/snjs'
|
||||
|
||||
export type FileViewProps = {
|
||||
application: WebApplication
|
||||
viewControllerManager: ViewControllerManager
|
||||
file: FileItem
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ import RoundIconButton from '../Button/RoundIconButton'
|
||||
const SyncTimeoutNoDebounceMs = 100
|
||||
const SyncTimeoutDebounceMs = 350
|
||||
|
||||
const FileViewWithoutProtection = ({ application, viewControllerManager, file }: FileViewProps) => {
|
||||
const FileViewWithoutProtection = ({ application, file }: FileViewProps) => {
|
||||
const syncTimeoutRef = useRef<number>()
|
||||
const fileInfoButtonRef = useRef<HTMLButtonElement>(null)
|
||||
|
||||
@@ -30,7 +30,7 @@ const FileViewWithoutProtection = ({ application, viewControllerManager, file }:
|
||||
clearTimeout(syncTimeoutRef.current)
|
||||
}
|
||||
|
||||
const shouldNotDebounce = application.noAccount()
|
||||
const shouldNotDebounce = application.sessions.isSignedOut()
|
||||
const syncDebounceMs = shouldNotDebounce ? SyncTimeoutNoDebounceMs : SyncTimeoutDebounceMs
|
||||
|
||||
syncTimeoutRef.current = window.setTimeout(async () => {
|
||||
@@ -52,7 +52,7 @@ const FileViewWithoutProtection = ({ application, viewControllerManager, file }:
|
||||
addDragTarget(target, {
|
||||
tooltipText: 'Drop your files to upload and link them to the current file',
|
||||
async callback(uploadedFile) {
|
||||
await viewControllerManager.linkingController.linkItems(uploadedFile, file)
|
||||
await application.linkingController.linkItems(uploadedFile, file)
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -62,7 +62,7 @@ const FileViewWithoutProtection = ({ application, viewControllerManager, file }:
|
||||
removeDragTarget(target)
|
||||
}
|
||||
}
|
||||
}, [addDragTarget, file, removeDragTarget, viewControllerManager.linkingController])
|
||||
}, [addDragTarget, file, removeDragTarget, application.linkingController])
|
||||
|
||||
return (
|
||||
<div className="sn-component section editor" aria-label="File" ref={fileDragTargetRef}>
|
||||
@@ -89,11 +89,7 @@ const FileViewWithoutProtection = ({ application, viewControllerManager, file }:
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-3">
|
||||
<LinkedItemsButton
|
||||
filesController={viewControllerManager.filesController}
|
||||
linkingController={viewControllerManager.linkingController}
|
||||
featuresController={viewControllerManager.featuresController}
|
||||
/>
|
||||
<LinkedItemsButton linkingController={application.linkingController} />
|
||||
<RoundIconButton
|
||||
label="File information panel"
|
||||
onClick={toggleFileInfoPanel}
|
||||
@@ -110,16 +106,11 @@ const FileViewWithoutProtection = ({ application, viewControllerManager, file }:
|
||||
>
|
||||
<FilePreviewInfoPanel file={file} />
|
||||
</Popover>
|
||||
<FileOptionsPanel
|
||||
filesController={viewControllerManager.filesController}
|
||||
selectionController={viewControllerManager.selectionController}
|
||||
linkingController={viewControllerManager.linkingController}
|
||||
navigationController={viewControllerManager.navigationController}
|
||||
/>
|
||||
<FileOptionsPanel itemListController={application.itemListController} />
|
||||
</div>
|
||||
</div>
|
||||
<div className="hidden md:flex">
|
||||
<LinkedItemBubblesContainer item={file} linkingController={viewControllerManager.linkingController} />
|
||||
<LinkedItemBubblesContainer item={file} linkingController={application.linkingController} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -12,16 +12,7 @@ type Props = AccountMenuProps & {
|
||||
user: unknown
|
||||
}
|
||||
|
||||
const AccountMenuButton = ({
|
||||
application,
|
||||
hasError,
|
||||
isOpen,
|
||||
mainApplicationGroup,
|
||||
onClickOutside,
|
||||
toggleMenu,
|
||||
user,
|
||||
viewControllerManager,
|
||||
}: Props) => {
|
||||
const AccountMenuButton = ({ hasError, isOpen, mainApplicationGroup, onClickOutside, toggleMenu, user }: Props) => {
|
||||
const buttonRef = useRef<HTMLButtonElement>(null)
|
||||
|
||||
return (
|
||||
@@ -49,12 +40,7 @@ const AccountMenuButton = ({
|
||||
align="start"
|
||||
className="py-2"
|
||||
>
|
||||
<AccountMenu
|
||||
onClickOutside={onClickOutside}
|
||||
viewControllerManager={viewControllerManager}
|
||||
application={application}
|
||||
mainApplicationGroup={mainApplicationGroup}
|
||||
/>
|
||||
<AccountMenu onClickOutside={onClickOutside} mainApplicationGroup={mainApplicationGroup} />
|
||||
</Popover>
|
||||
</>
|
||||
)
|
||||
|
||||
@@ -125,12 +125,10 @@ class Footer extends AbstractComponent<Props, State> {
|
||||
})
|
||||
|
||||
this.autorun(() => {
|
||||
const showBetaWarning = this.viewControllerManager.showBetaWarning
|
||||
this.setState({
|
||||
showBetaWarning: showBetaWarning,
|
||||
showAccountMenu: this.viewControllerManager.accountMenuController.show,
|
||||
showQuickSettingsMenu: this.viewControllerManager.quickSettingsMenuController.open,
|
||||
showVaultSelectionMenu: this.viewControllerManager.vaultSelectionController.open,
|
||||
showAccountMenu: this.application.accountMenuController.show,
|
||||
showQuickSettingsMenu: this.application.quickSettingsMenuController.open,
|
||||
showVaultSelectionMenu: this.application.vaultSelectionController.open,
|
||||
})
|
||||
})
|
||||
}
|
||||
@@ -156,7 +154,7 @@ class Footer extends AbstractComponent<Props, State> {
|
||||
}
|
||||
|
||||
reloadUser() {
|
||||
this.user = this.application.getUser()
|
||||
this.user = this.application.sessions.getUser()
|
||||
}
|
||||
|
||||
async reloadPasscodeStatus() {
|
||||
@@ -194,7 +192,7 @@ class Footer extends AbstractComponent<Props, State> {
|
||||
if (!this.didCheckForOffline) {
|
||||
this.didCheckForOffline = true
|
||||
if (this.state.offline && this.application.items.getNoteCount() === 0) {
|
||||
this.viewControllerManager.accountMenuController.setShow(true)
|
||||
this.application.accountMenuController.setShow(true)
|
||||
}
|
||||
}
|
||||
this.findErrors()
|
||||
@@ -270,7 +268,7 @@ class Footer extends AbstractComponent<Props, State> {
|
||||
|
||||
updateOfflineStatus() {
|
||||
this.setState({
|
||||
offline: this.application.noAccount(),
|
||||
offline: this.application.sessions.isSignedOut(),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -295,15 +293,15 @@ class Footer extends AbstractComponent<Props, State> {
|
||||
}
|
||||
|
||||
accountMenuClickHandler = () => {
|
||||
this.viewControllerManager.accountMenuController.toggleShow()
|
||||
this.application.accountMenuController.toggleShow()
|
||||
}
|
||||
|
||||
quickSettingsClickHandler = () => {
|
||||
this.viewControllerManager.quickSettingsMenuController.toggle()
|
||||
this.application.quickSettingsMenuController.toggle()
|
||||
}
|
||||
|
||||
vaultSelectionClickHandler = () => {
|
||||
this.viewControllerManager.vaultSelectionController.toggle()
|
||||
this.application.vaultSelectionController.toggle()
|
||||
}
|
||||
|
||||
syncResolutionClickHandler = () => {
|
||||
@@ -313,8 +311,8 @@ class Footer extends AbstractComponent<Props, State> {
|
||||
}
|
||||
|
||||
closeAccountMenu = () => {
|
||||
this.viewControllerManager.accountMenuController.setShow(false)
|
||||
this.viewControllerManager.accountMenuController.setCurrentPane(AccountMenuPane.GeneralMenu)
|
||||
this.application.accountMenuController.setShow(false)
|
||||
this.application.accountMenuController.setCurrentPane(AccountMenuPane.GeneralMenu)
|
||||
}
|
||||
|
||||
lockClickHandler = () => {
|
||||
@@ -342,19 +340,19 @@ class Footer extends AbstractComponent<Props, State> {
|
||||
}
|
||||
|
||||
clickOutsideAccountMenu = () => {
|
||||
this.viewControllerManager.accountMenuController.closeAccountMenu()
|
||||
this.application.accountMenuController.closeAccountMenu()
|
||||
}
|
||||
|
||||
clickOutsideQuickSettingsMenu = () => {
|
||||
this.viewControllerManager.quickSettingsMenuController.closeQuickSettingsMenu()
|
||||
this.application.quickSettingsMenuController.closeQuickSettingsMenu()
|
||||
}
|
||||
|
||||
openPreferences = (openWhatsNew: boolean) => {
|
||||
this.clickOutsideQuickSettingsMenu()
|
||||
if (openWhatsNew) {
|
||||
this.viewControllerManager.preferencesController.setCurrentPane('whats-new')
|
||||
this.application.preferencesController.setCurrentPane('whats-new')
|
||||
}
|
||||
this.viewControllerManager.preferencesController.openPreferences()
|
||||
this.application.preferencesController.openPreferences()
|
||||
}
|
||||
|
||||
override render() {
|
||||
@@ -367,14 +365,12 @@ class Footer extends AbstractComponent<Props, State> {
|
||||
<div className="left flex h-full flex-shrink-0">
|
||||
<div className="sk-app-bar-item relative z-footer-bar-item ml-0 select-none">
|
||||
<AccountMenuButton
|
||||
application={this.application}
|
||||
hasError={this.state.hasError}
|
||||
isOpen={this.state.showAccountMenu}
|
||||
mainApplicationGroup={this.props.applicationGroup}
|
||||
onClickOutside={this.clickOutsideAccountMenu}
|
||||
toggleMenu={this.accountMenuClickHandler}
|
||||
user={this.user}
|
||||
viewControllerManager={this.viewControllerManager}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -387,7 +383,7 @@ class Footer extends AbstractComponent<Props, State> {
|
||||
isOpen={this.state.showQuickSettingsMenu}
|
||||
toggleMenu={this.quickSettingsClickHandler}
|
||||
application={this.application}
|
||||
quickSettingsMenuController={this.viewControllerManager.quickSettingsMenuController}
|
||||
quickSettingsMenuController={this.application.quickSettingsMenuController}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -395,13 +391,13 @@ class Footer extends AbstractComponent<Props, State> {
|
||||
<VaultSelectionButton
|
||||
isOpen={this.state.showVaultSelectionMenu}
|
||||
toggleMenu={this.vaultSelectionClickHandler}
|
||||
controller={this.viewControllerManager.vaultSelectionController}
|
||||
controller={this.application.vaultSelectionController}
|
||||
/>
|
||||
</div>
|
||||
<UpgradeNow
|
||||
application={this.application}
|
||||
featuresController={this.viewControllerManager.featuresController}
|
||||
subscriptionContoller={this.viewControllerManager.subscriptionController}
|
||||
featuresController={this.application.featuresController}
|
||||
subscriptionContoller={this.application.subscriptionController}
|
||||
/>
|
||||
{this.state.showBetaWarning && (
|
||||
<Fragment>
|
||||
|
||||
@@ -5,9 +5,12 @@ import ImportModalInitialPage from './InitialPage'
|
||||
import Modal, { ModalAction } from '../Modal/Modal'
|
||||
import ModalOverlay from '../Modal/ModalOverlay'
|
||||
import { ImportModalController } from '@/Controllers/ImportModalController'
|
||||
import { useApplication } from '../ApplicationProvider'
|
||||
|
||||
const ImportModal = ({ importModalController }: { importModalController: ImportModalController }) => {
|
||||
const { files, setFiles, updateFile, removeFile, importer, parseAndImport, isVisible, close } = importModalController
|
||||
const application = useApplication()
|
||||
|
||||
const { files, setFiles, updateFile, removeFile, parseAndImport, isVisible, close } = importModalController
|
||||
|
||||
const isReadyToImport = files.length > 0 && files.every((file) => file.status === 'ready')
|
||||
const importSuccessOrError =
|
||||
@@ -45,7 +48,7 @@ const ImportModal = ({ importModalController }: { importModalController: ImportM
|
||||
key={file.id}
|
||||
updateFile={updateFile}
|
||||
removeFile={removeFile}
|
||||
importer={importer}
|
||||
importer={application.importer}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import { FeaturesController } from '@/Controllers/FeaturesController'
|
||||
import { FilesController } from '@/Controllers/FilesController'
|
||||
import { LinkingController } from '@/Controllers/LinkingController'
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import { useRef, useCallback } from 'react'
|
||||
@@ -10,11 +8,9 @@ import LinkedItemsPanel from './LinkedItemsPanel'
|
||||
type Props = {
|
||||
linkingController: LinkingController
|
||||
onClickPreprocessing?: () => Promise<void>
|
||||
filesController: FilesController
|
||||
featuresController: FeaturesController
|
||||
}
|
||||
|
||||
const LinkedItemsButton = ({ linkingController, filesController, onClickPreprocessing, featuresController }: Props) => {
|
||||
const LinkedItemsButton = ({ linkingController, onClickPreprocessing }: Props) => {
|
||||
const { activeItem, isLinkingPanelOpen, setIsLinkingPanelOpen } = linkingController
|
||||
const buttonRef = useRef<HTMLButtonElement>(null)
|
||||
|
||||
@@ -40,13 +36,7 @@ const LinkedItemsButton = ({ linkingController, filesController, onClickPreproce
|
||||
open={isLinkingPanelOpen}
|
||||
className="pb-2"
|
||||
>
|
||||
<LinkedItemsPanel
|
||||
item={activeItem}
|
||||
isOpen={isLinkingPanelOpen}
|
||||
linkingController={linkingController}
|
||||
filesController={filesController}
|
||||
featuresController={featuresController}
|
||||
/>
|
||||
<LinkedItemsPanel item={activeItem} isOpen={isLinkingPanelOpen} />
|
||||
</Popover>
|
||||
</>
|
||||
)
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
import { FeatureName } from '@/Controllers/FeatureName'
|
||||
import { FeaturesController } from '@/Controllers/FeaturesController'
|
||||
import { FilesController } from '@/Controllers/FilesController'
|
||||
import { LinkingController } from '@/Controllers/LinkingController'
|
||||
import { classNames } from '@standardnotes/utils'
|
||||
import { getLinkingSearchResults } from '@/Utils/Items/Search/getSearchResults'
|
||||
import { observer } from 'mobx-react-lite'
|
||||
@@ -15,26 +12,16 @@ import { LinkedItemsSectionItem } from './LinkedItemsSectionItem'
|
||||
import { DecryptedItem } from '@standardnotes/snjs'
|
||||
import { useItemLinks } from '@/Hooks/useItemLinks'
|
||||
|
||||
const LinkedItemsPanel = ({
|
||||
linkingController,
|
||||
filesController,
|
||||
featuresController,
|
||||
isOpen,
|
||||
item,
|
||||
}: {
|
||||
linkingController: LinkingController
|
||||
filesController: FilesController
|
||||
featuresController: FeaturesController
|
||||
isOpen: boolean
|
||||
item: DecryptedItem
|
||||
}) => {
|
||||
const { linkItems, unlinkItems, activateItem, createAndAddNewTag, isEntitledToNoteLinking } = linkingController
|
||||
const LinkedItemsPanel = ({ isOpen, item }: { isOpen: boolean; item: DecryptedItem }) => {
|
||||
const application = useApplication()
|
||||
|
||||
const { linkItems, unlinkItems, activateItem, createAndAddNewTag, isEntitledToNoteLinking } =
|
||||
application.linkingController
|
||||
|
||||
const { notesLinkedToItem, notesLinkingToItem, filesLinkedToItem, filesLinkingToItem, tagsLinkedToItem } =
|
||||
useItemLinks(item)
|
||||
|
||||
const { entitledToFiles } = featuresController
|
||||
const application = useApplication()
|
||||
const { entitledToFiles } = application.featuresController
|
||||
|
||||
const searchInputRef = useRef<HTMLInputElement | null>(null)
|
||||
const [searchQuery, setSearchQuery] = useState('')
|
||||
@@ -49,11 +36,11 @@ const LinkedItemsPanel = ({
|
||||
|
||||
const selectAndUploadFiles = async () => {
|
||||
if (!entitledToFiles) {
|
||||
void featuresController.showPremiumAlert(FeatureName.Files)
|
||||
void application.featuresController.showPremiumAlert(FeatureName.Files)
|
||||
return
|
||||
}
|
||||
|
||||
void filesController.selectAndUploadNewFiles((file) => {
|
||||
void application.filesController.selectAndUploadNewFiles((file) => {
|
||||
void linkItems(item, file)
|
||||
})
|
||||
}
|
||||
@@ -122,7 +109,7 @@ const LinkedItemsPanel = ({
|
||||
searchQuery={searchQuery}
|
||||
unlinkItem={() => unlinkItems(item, link.item)}
|
||||
activateItem={activateItem}
|
||||
handleFileAction={filesController.handleFileAction}
|
||||
handleFileAction={application.filesController.handleFileAction}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
@@ -142,7 +129,7 @@ const LinkedItemsPanel = ({
|
||||
searchQuery={searchQuery}
|
||||
unlinkItem={() => unlinkItems(item, link.item)}
|
||||
activateItem={activateItem}
|
||||
handleFileAction={filesController.handleFileAction}
|
||||
handleFileAction={application.filesController.handleFileAction}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
@@ -166,7 +153,7 @@ const LinkedItemsPanel = ({
|
||||
searchQuery={searchQuery}
|
||||
unlinkItem={() => unlinkItems(item, link.item)}
|
||||
activateItem={activateItem}
|
||||
handleFileAction={filesController.handleFileAction}
|
||||
handleFileAction={application.filesController.handleFileAction}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
@@ -185,7 +172,7 @@ const LinkedItemsPanel = ({
|
||||
searchQuery={searchQuery}
|
||||
unlinkItem={() => unlinkItems(item, link.item)}
|
||||
activateItem={activateItem}
|
||||
handleFileAction={filesController.handleFileAction}
|
||||
handleFileAction={application.filesController.handleFileAction}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
@@ -202,7 +189,7 @@ const LinkedItemsPanel = ({
|
||||
searchQuery={searchQuery}
|
||||
unlinkItem={() => unlinkItems(item, link.item)}
|
||||
activateItem={activateItem}
|
||||
handleFileAction={filesController.handleFileAction}
|
||||
handleFileAction={application.filesController.handleFileAction}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
@@ -221,7 +208,7 @@ const LinkedItemsPanel = ({
|
||||
searchQuery={searchQuery}
|
||||
unlinkItem={() => unlinkItems(item, link.item)}
|
||||
activateItem={activateItem}
|
||||
handleFileAction={filesController.handleFileAction}
|
||||
handleFileAction={application.filesController.handleFileAction}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
||||
@@ -3,41 +3,25 @@ import { observer } from 'mobx-react-lite'
|
||||
import Button from '../Button/Button'
|
||||
import { useCallback } from 'react'
|
||||
import FileOptionsPanel from '../FileContextMenu/FileOptionsPanel'
|
||||
import { FilesController } from '@/Controllers/FilesController'
|
||||
import { SelectedItemsController } from '@/Controllers/SelectedItemsController'
|
||||
import { NavigationController } from '@/Controllers/Navigation/NavigationController'
|
||||
import { LinkingController } from '@/Controllers/LinkingController'
|
||||
import { ItemListController } from '@/Controllers/ItemList/ItemListController'
|
||||
|
||||
type Props = {
|
||||
filesController: FilesController
|
||||
selectionController: SelectedItemsController
|
||||
linkingController: LinkingController
|
||||
navigationController: NavigationController
|
||||
itemListController: ItemListController
|
||||
}
|
||||
|
||||
const MultipleSelectedFiles = ({
|
||||
filesController,
|
||||
selectionController,
|
||||
linkingController,
|
||||
navigationController,
|
||||
}: Props) => {
|
||||
const count = selectionController.selectedFilesCount
|
||||
const MultipleSelectedFiles = ({ itemListController }: Props) => {
|
||||
const count = itemListController.selectedFilesCount
|
||||
|
||||
const cancelMultipleSelection = useCallback(() => {
|
||||
selectionController.cancelMultipleSelection()
|
||||
}, [selectionController])
|
||||
itemListController.cancelMultipleSelection()
|
||||
}, [itemListController])
|
||||
|
||||
return (
|
||||
<div className="flex h-full flex-col items-center">
|
||||
<div className="flex w-full items-center justify-between p-4">
|
||||
<h1 className="m-0 text-lg font-bold">{count} selected files</h1>
|
||||
<div>
|
||||
<FileOptionsPanel
|
||||
filesController={filesController}
|
||||
selectionController={selectionController}
|
||||
linkingController={linkingController}
|
||||
navigationController={navigationController}
|
||||
/>
|
||||
<FileOptionsPanel itemListController={itemListController} />
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex min-h-full w-full max-w-md flex-grow flex-col items-center justify-center">
|
||||
|
||||
@@ -5,35 +5,19 @@ import { WebApplication } from '@/Application/WebApplication'
|
||||
import PinNoteButton from '@/Components/PinNoteButton/PinNoteButton'
|
||||
import Button from '../Button/Button'
|
||||
import { useCallback } from 'react'
|
||||
import { NavigationController } from '@/Controllers/Navigation/NavigationController'
|
||||
import { NotesController } from '@/Controllers/NotesController/NotesController'
|
||||
import { SelectedItemsController } from '@/Controllers/SelectedItemsController'
|
||||
import { HistoryModalController } from '@/Controllers/NoteHistory/HistoryModalController'
|
||||
import { LinkingController } from '@/Controllers/LinkingController'
|
||||
import ChangeMultipleButton from '../ChangeEditor/ChangeMultipleButton'
|
||||
|
||||
type Props = {
|
||||
application: WebApplication
|
||||
navigationController: NavigationController
|
||||
notesController: NotesController
|
||||
selectionController: SelectedItemsController
|
||||
historyModalController: HistoryModalController
|
||||
linkingController: LinkingController
|
||||
}
|
||||
|
||||
const MultipleSelectedNotes = ({
|
||||
application,
|
||||
navigationController,
|
||||
notesController,
|
||||
linkingController,
|
||||
selectionController,
|
||||
historyModalController,
|
||||
}: Props) => {
|
||||
const MultipleSelectedNotes = ({ application }: Props) => {
|
||||
const { notesController, itemListController } = application
|
||||
const count = notesController.selectedNotesCount
|
||||
|
||||
const cancelMultipleSelection = useCallback(() => {
|
||||
selectionController.cancelMultipleSelection()
|
||||
}, [selectionController])
|
||||
itemListController.cancelMultipleSelection()
|
||||
}, [itemListController])
|
||||
|
||||
return (
|
||||
<div className="flex h-full flex-col items-center">
|
||||
@@ -46,13 +30,7 @@ const MultipleSelectedNotes = ({
|
||||
<div className="mr-3">
|
||||
<PinNoteButton notesController={notesController} />
|
||||
</div>
|
||||
<NotesOptionsPanel
|
||||
navigationController={navigationController}
|
||||
notesController={notesController}
|
||||
linkingController={linkingController}
|
||||
historyModalController={historyModalController}
|
||||
selectionController={selectionController}
|
||||
/>
|
||||
<NotesOptionsPanel notesController={notesController} />
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex min-h-full w-full max-w-md flex-grow flex-col items-center justify-center md:min-h-0">
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { FileItem } from '@standardnotes/snjs'
|
||||
import { AbstractComponent } from '@/Components/Abstract/PureComponent'
|
||||
import { WebApplication } from '@/Application/WebApplication'
|
||||
import MultipleSelectedNotes from '@/Components/MultipleSelectedNotes/MultipleSelectedNotes'
|
||||
import MultipleSelectedFiles from '../MultipleSelectedFiles/MultipleSelectedFiles'
|
||||
import { AppPaneId } from '../Panes/AppPaneMetadata'
|
||||
@@ -8,6 +7,7 @@ import FileView from '../FileView/FileView'
|
||||
import NoteView from '../NoteView/NoteView'
|
||||
import { NoteViewController } from '../NoteView/Controller/NoteViewController'
|
||||
import { FileViewController } from '../NoteView/Controller/FileViewController'
|
||||
import { WebApplication } from '@/Application/WebApplication'
|
||||
|
||||
type State = {
|
||||
showMultipleSelectedNotes: boolean
|
||||
@@ -48,36 +48,32 @@ class NoteGroupView extends AbstractComponent<Props, State> {
|
||||
})
|
||||
|
||||
this.autorun(() => {
|
||||
if (!this.viewControllerManager) {
|
||||
return
|
||||
}
|
||||
|
||||
if (this.viewControllerManager && this.viewControllerManager.notesController) {
|
||||
if (this.application.notesController) {
|
||||
this.setState({
|
||||
showMultipleSelectedNotes: this.viewControllerManager.notesController.selectedNotesCount > 1,
|
||||
showMultipleSelectedNotes: this.application.notesController.selectedNotesCount > 1,
|
||||
})
|
||||
}
|
||||
|
||||
if (this.viewControllerManager.selectionController) {
|
||||
if (this.application.itemListController) {
|
||||
this.setState({
|
||||
showMultipleSelectedFiles: this.viewControllerManager.selectionController.selectedFilesCount > 1,
|
||||
showMultipleSelectedFiles: this.application.itemListController.selectedFilesCount > 1,
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
this.autorun(() => {
|
||||
if (this.viewControllerManager && this.viewControllerManager.selectionController) {
|
||||
if (this.application.itemListController) {
|
||||
this.setState({
|
||||
selectedFile: this.viewControllerManager.selectionController.selectedFiles[0],
|
||||
selectedFile: this.application.itemListController.selectedFiles[0],
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
this.autorun(() => {
|
||||
if (this.viewControllerManager && this.viewControllerManager.paneController) {
|
||||
if (this.application.paneController) {
|
||||
this.setState({
|
||||
selectedPane: this.viewControllerManager.paneController.currentPane,
|
||||
isInMobileView: this.viewControllerManager.paneController.isInMobileView,
|
||||
selectedPane: this.application.paneController.currentPane,
|
||||
isInMobileView: this.application.paneController.isInMobileView,
|
||||
})
|
||||
}
|
||||
})
|
||||
@@ -98,23 +94,9 @@ class NoteGroupView extends AbstractComponent<Props, State> {
|
||||
|
||||
return (
|
||||
<>
|
||||
{this.state.showMultipleSelectedNotes && (
|
||||
<MultipleSelectedNotes
|
||||
application={this.application}
|
||||
selectionController={this.viewControllerManager.selectionController}
|
||||
navigationController={this.viewControllerManager.navigationController}
|
||||
notesController={this.viewControllerManager.notesController}
|
||||
linkingController={this.viewControllerManager.linkingController}
|
||||
historyModalController={this.viewControllerManager.historyModalController}
|
||||
/>
|
||||
)}
|
||||
{this.state.showMultipleSelectedNotes && <MultipleSelectedNotes application={this.application} />}
|
||||
{this.state.showMultipleSelectedFiles && (
|
||||
<MultipleSelectedFiles
|
||||
filesController={this.viewControllerManager.filesController}
|
||||
selectionController={this.viewControllerManager.selectionController}
|
||||
navigationController={this.viewControllerManager.navigationController}
|
||||
linkingController={this.viewControllerManager.linkingController}
|
||||
/>
|
||||
<MultipleSelectedFiles itemListController={this.application.itemListController} />
|
||||
)}
|
||||
{shouldNotShowMultipleSelectedItems && hasControllers && (
|
||||
<>
|
||||
@@ -122,12 +104,7 @@ class NoteGroupView extends AbstractComponent<Props, State> {
|
||||
return controller instanceof NoteViewController ? (
|
||||
<NoteView key={controller.runtimeId} application={this.application} controller={controller} />
|
||||
) : (
|
||||
<FileView
|
||||
key={controller.runtimeId}
|
||||
application={this.application}
|
||||
viewControllerManager={this.viewControllerManager}
|
||||
file={controller.item}
|
||||
/>
|
||||
<FileView key={controller.runtimeId} application={this.application} file={controller.item} />
|
||||
)
|
||||
})}
|
||||
</>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { FileItem } from '@standardnotes/models'
|
||||
import { ContentType } from '@standardnotes/domain-core'
|
||||
import { SNApplication } from '@standardnotes/snjs'
|
||||
import { ItemViewControllerInterface } from './ItemViewControllerInterface'
|
||||
import { ItemManagerInterface } from '@standardnotes/snjs'
|
||||
|
||||
export class FileViewController implements ItemViewControllerInterface {
|
||||
public dealloced = false
|
||||
@@ -9,15 +9,14 @@ export class FileViewController implements ItemViewControllerInterface {
|
||||
public runtimeId = `${Math.random()}`
|
||||
|
||||
constructor(
|
||||
private application: SNApplication,
|
||||
public item: FileItem,
|
||||
private items: ItemManagerInterface,
|
||||
) {}
|
||||
|
||||
deinit() {
|
||||
this.dealloced = true
|
||||
this.removeStreamObserver?.()
|
||||
;(this.removeStreamObserver as unknown) = undefined
|
||||
;(this.application as unknown) = undefined
|
||||
;(this.item as unknown) = undefined
|
||||
}
|
||||
|
||||
@@ -26,23 +25,20 @@ export class FileViewController implements ItemViewControllerInterface {
|
||||
}
|
||||
|
||||
private streamItems() {
|
||||
this.removeStreamObserver = this.application.streamItems<FileItem>(
|
||||
ContentType.TYPES.File,
|
||||
({ changed, inserted }) => {
|
||||
if (this.dealloced) {
|
||||
return
|
||||
}
|
||||
this.removeStreamObserver = this.items.streamItems<FileItem>(ContentType.TYPES.File, ({ changed, inserted }) => {
|
||||
if (this.dealloced) {
|
||||
return
|
||||
}
|
||||
|
||||
const files = changed.concat(inserted)
|
||||
const files = changed.concat(inserted)
|
||||
|
||||
const matchingFile = files.find((item) => {
|
||||
return item.uuid === this.item.uuid
|
||||
})
|
||||
const matchingFile = files.find((item) => {
|
||||
return item.uuid === this.item.uuid
|
||||
})
|
||||
|
||||
if (matchingFile) {
|
||||
this.item = matchingFile
|
||||
}
|
||||
},
|
||||
)
|
||||
if (matchingFile) {
|
||||
this.item = matchingFile
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,19 @@
|
||||
import { WebApplication } from '@/Application/WebApplication'
|
||||
import { removeFromArray } from '@standardnotes/utils'
|
||||
import { FileItem, SNNote } from '@standardnotes/snjs'
|
||||
import {
|
||||
AlertService,
|
||||
ComponentManagerInterface,
|
||||
FileItem,
|
||||
ItemManagerInterface,
|
||||
MutatorClientInterface,
|
||||
PreferenceServiceInterface,
|
||||
SNNote,
|
||||
SessionsClientInterface,
|
||||
SyncServiceInterface,
|
||||
} from '@standardnotes/snjs'
|
||||
import { NoteViewController } from './NoteViewController'
|
||||
import { FileViewController } from './FileViewController'
|
||||
import { TemplateNoteViewControllerOptions } from './TemplateNoteViewControllerOptions'
|
||||
import { IsNativeMobileWeb } from '@standardnotes/ui-services'
|
||||
|
||||
type ItemControllerGroupChangeCallback = (activeController: NoteViewController | FileViewController | undefined) => void
|
||||
|
||||
@@ -12,10 +22,19 @@ export class ItemGroupController {
|
||||
changeObservers: ItemControllerGroupChangeCallback[] = []
|
||||
eventObservers: (() => void)[] = []
|
||||
|
||||
constructor(private application: WebApplication) {}
|
||||
constructor(
|
||||
private items: ItemManagerInterface,
|
||||
private mutator: MutatorClientInterface,
|
||||
private sync: SyncServiceInterface,
|
||||
private sessions: SessionsClientInterface,
|
||||
private preferences: PreferenceServiceInterface,
|
||||
private components: ComponentManagerInterface,
|
||||
private alerts: AlertService,
|
||||
private _isNativeMobileWeb: IsNativeMobileWeb,
|
||||
) {}
|
||||
|
||||
public deinit(): void {
|
||||
;(this.application as unknown) = undefined
|
||||
;(this.items as unknown) = undefined
|
||||
|
||||
this.eventObservers.forEach((removeObserver) => {
|
||||
removeObserver()
|
||||
@@ -42,11 +61,32 @@ export class ItemGroupController {
|
||||
let controller!: NoteViewController | FileViewController
|
||||
|
||||
if (context.file) {
|
||||
controller = new FileViewController(this.application, context.file)
|
||||
controller = new FileViewController(context.file, this.items)
|
||||
} else if (context.note) {
|
||||
controller = new NoteViewController(this.application, context.note)
|
||||
controller = new NoteViewController(
|
||||
context.note,
|
||||
this.items,
|
||||
this.mutator,
|
||||
this.sync,
|
||||
this.sessions,
|
||||
this.preferences,
|
||||
this.components,
|
||||
this.alerts,
|
||||
this._isNativeMobileWeb,
|
||||
)
|
||||
} else if (context.templateOptions) {
|
||||
controller = new NoteViewController(this.application, undefined, context.templateOptions)
|
||||
controller = new NoteViewController(
|
||||
undefined,
|
||||
this.items,
|
||||
this.mutator,
|
||||
this.sync,
|
||||
this.sessions,
|
||||
this.preferences,
|
||||
this.components,
|
||||
this.alerts,
|
||||
this._isNativeMobileWeb,
|
||||
context.templateOptions,
|
||||
)
|
||||
} else {
|
||||
throw Error('Invalid input to createItemController')
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
SyncServiceInterface,
|
||||
ItemManagerInterface,
|
||||
MutatorClientInterface,
|
||||
PreferenceServiceInterface,
|
||||
} from '@standardnotes/snjs'
|
||||
import { NativeFeatureIdentifier, NoteType } from '@standardnotes/features'
|
||||
import { NoteViewController } from './NoteViewController'
|
||||
@@ -18,24 +19,24 @@ describe('note view controller', () => {
|
||||
let componentManager: SNComponentManager
|
||||
|
||||
beforeEach(() => {
|
||||
application = {} as jest.Mocked<WebApplication>
|
||||
application.streamItems = jest.fn().mockReturnValue(() => {})
|
||||
application.getPreference = jest.fn().mockReturnValue(true)
|
||||
application.noAccount = jest.fn().mockReturnValue(false)
|
||||
application.isNativeMobileWeb = jest.fn().mockReturnValue(false)
|
||||
application = {
|
||||
preferences: {
|
||||
getValue: jest.fn().mockReturnValue(true),
|
||||
} as unknown as jest.Mocked<PreferenceServiceInterface>,
|
||||
items: {
|
||||
streamItems: jest.fn().mockReturnValue(() => {}),
|
||||
createTemplateItem: jest.fn().mockReturnValue({} as SNNote),
|
||||
} as unknown as jest.Mocked<ItemManagerInterface>,
|
||||
mutator: {} as jest.Mocked<MutatorClientInterface>,
|
||||
} as unknown as jest.Mocked<WebApplication>
|
||||
|
||||
const items = {} as jest.Mocked<ItemManagerInterface>
|
||||
items.createTemplateItem = jest.fn().mockReturnValue({} as SNNote)
|
||||
Object.defineProperty(application, 'items', { value: items })
|
||||
application.isNativeMobileWeb = jest.fn().mockReturnValue(false)
|
||||
|
||||
Object.defineProperty(application, 'sync', { value: {} as jest.Mocked<SyncServiceInterface> })
|
||||
application.sync.sync = jest.fn().mockReturnValue(Promise.resolve())
|
||||
|
||||
componentManager = {} as jest.Mocked<SNComponentManager>
|
||||
Object.defineProperty(application, 'componentManager', { value: componentManager })
|
||||
|
||||
const mutator = {} as jest.Mocked<MutatorClientInterface>
|
||||
Object.defineProperty(application, 'mutator', { value: mutator })
|
||||
})
|
||||
|
||||
it('should create notes with plaintext note type', async () => {
|
||||
@@ -43,7 +44,17 @@ describe('note view controller', () => {
|
||||
.fn()
|
||||
.mockReturnValue(NativeFeatureIdentifier.TYPES.PlainEditor)
|
||||
|
||||
const controller = new NoteViewController(application)
|
||||
const controller = new NoteViewController(
|
||||
undefined,
|
||||
application.items,
|
||||
application.mutator,
|
||||
application.sync,
|
||||
application.sessions,
|
||||
application.preferences,
|
||||
application.componentManager,
|
||||
application.alerts,
|
||||
application.isNativeMobileWebUseCase,
|
||||
)
|
||||
await controller.initialize()
|
||||
|
||||
expect(application.items.createTemplateItem).toHaveBeenCalledWith(
|
||||
@@ -64,7 +75,17 @@ describe('note view controller', () => {
|
||||
.fn()
|
||||
.mockReturnValue(NativeFeatureIdentifier.TYPES.MarkdownProEditor)
|
||||
|
||||
const controller = new NoteViewController(application)
|
||||
const controller = new NoteViewController(
|
||||
undefined,
|
||||
application.items,
|
||||
application.mutator,
|
||||
application.sync,
|
||||
application.sessions,
|
||||
application.preferences,
|
||||
application.componentManager,
|
||||
application.alerts,
|
||||
application.isNativeMobileWebUseCase,
|
||||
)
|
||||
await controller.initialize()
|
||||
|
||||
expect(application.items.createTemplateItem).toHaveBeenCalledWith(
|
||||
@@ -86,7 +107,18 @@ describe('note view controller', () => {
|
||||
application.items.findItem = jest.fn().mockReturnValue(tag)
|
||||
application.mutator.addTagToNote = jest.fn()
|
||||
|
||||
const controller = new NoteViewController(application, undefined, { tag: tag.uuid })
|
||||
const controller = new NoteViewController(
|
||||
undefined,
|
||||
application.items,
|
||||
application.mutator,
|
||||
application.sync,
|
||||
application.sessions,
|
||||
application.preferences,
|
||||
application.componentManager,
|
||||
application.alerts,
|
||||
application.isNativeMobileWebUseCase,
|
||||
{ tag: tag.uuid },
|
||||
)
|
||||
await controller.initialize()
|
||||
|
||||
expect(controller['defaultTag']).toEqual(tag)
|
||||
@@ -100,7 +132,17 @@ describe('note view controller', () => {
|
||||
|
||||
application.items.findItem = jest.fn().mockReturnValue(note)
|
||||
|
||||
const controller = new NoteViewController(application, note)
|
||||
const controller = new NoteViewController(
|
||||
note,
|
||||
application.items,
|
||||
application.mutator,
|
||||
application.sync,
|
||||
application.sessions,
|
||||
application.preferences,
|
||||
application.componentManager,
|
||||
application.alerts,
|
||||
application.isNativeMobileWebUseCase,
|
||||
)
|
||||
await controller.initialize()
|
||||
|
||||
const changePromise = Deferred()
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { WebApplication } from '@/Application/WebApplication'
|
||||
import { noteTypeForEditorIdentifier } from '@standardnotes/features'
|
||||
import {
|
||||
SNNote,
|
||||
@@ -9,13 +8,23 @@ import {
|
||||
PrefKey,
|
||||
PayloadVaultOverrides,
|
||||
} from '@standardnotes/models'
|
||||
import { UuidString } from '@standardnotes/snjs'
|
||||
import {
|
||||
AlertService,
|
||||
ComponentManagerInterface,
|
||||
ItemManagerInterface,
|
||||
MutatorClientInterface,
|
||||
PreferenceServiceInterface,
|
||||
SessionsClientInterface,
|
||||
SyncServiceInterface,
|
||||
UuidString,
|
||||
} from '@standardnotes/snjs'
|
||||
import { removeFromArray } from '@standardnotes/utils'
|
||||
import { ContentType } from '@standardnotes/domain-core'
|
||||
import { ItemViewControllerInterface } from './ItemViewControllerInterface'
|
||||
import { TemplateNoteViewControllerOptions } from './TemplateNoteViewControllerOptions'
|
||||
import { log, LoggingDomain } from '@/Logging'
|
||||
import { NoteSaveFunctionParams, NoteSyncController } from '../../../Controllers/NoteSyncController'
|
||||
import { IsNativeMobileWeb } from '@standardnotes/ui-services'
|
||||
|
||||
export type EditorValues = {
|
||||
title: string
|
||||
@@ -37,8 +46,15 @@ export class NoteViewController implements ItemViewControllerInterface {
|
||||
private syncController!: NoteSyncController
|
||||
|
||||
constructor(
|
||||
private application: WebApplication,
|
||||
item?: SNNote,
|
||||
item: SNNote | undefined,
|
||||
private items: ItemManagerInterface,
|
||||
private mutator: MutatorClientInterface,
|
||||
private sync: SyncServiceInterface,
|
||||
private sessions: SessionsClientInterface,
|
||||
private preferences: PreferenceServiceInterface,
|
||||
private components: ComponentManagerInterface,
|
||||
private alerts: AlertService,
|
||||
private _isNativeMobileWeb: IsNativeMobileWeb,
|
||||
public templateNoteOptions?: TemplateNoteViewControllerOptions,
|
||||
) {
|
||||
if (item) {
|
||||
@@ -50,10 +66,18 @@ export class NoteViewController implements ItemViewControllerInterface {
|
||||
}
|
||||
|
||||
if (this.defaultTagUuid) {
|
||||
this.defaultTag = this.application.items.findItem(this.defaultTagUuid) as SNTag
|
||||
this.defaultTag = this.items.findItem(this.defaultTagUuid) as SNTag
|
||||
}
|
||||
|
||||
this.syncController = new NoteSyncController(this.application, this.item)
|
||||
this.syncController = new NoteSyncController(
|
||||
this.item,
|
||||
this.items,
|
||||
this.mutator,
|
||||
this.sessions,
|
||||
this.sync,
|
||||
this.alerts,
|
||||
this._isNativeMobileWeb,
|
||||
)
|
||||
}
|
||||
|
||||
deinit(): void {
|
||||
@@ -74,9 +98,6 @@ export class NoteViewController implements ItemViewControllerInterface {
|
||||
disposer()
|
||||
}
|
||||
this.disposers.length = 0
|
||||
;(this.application as unknown) = undefined
|
||||
;(this.item as unknown) = undefined
|
||||
|
||||
this.innerValueChangeObservers.length = 0
|
||||
}
|
||||
|
||||
@@ -89,16 +110,16 @@ export class NoteViewController implements ItemViewControllerInterface {
|
||||
|
||||
this.needsInit = false
|
||||
|
||||
const addTagHierarchy = this.application.getPreference(PrefKey.NoteAddToParentFolders, true)
|
||||
const addTagHierarchy = this.preferences.getValue(PrefKey.NoteAddToParentFolders, true)
|
||||
|
||||
if (!this.item) {
|
||||
log(LoggingDomain.NoteView, 'Initializing as template note')
|
||||
|
||||
const editorIdentifier = this.application.componentManager.getDefaultEditorIdentifier(this.defaultTag)
|
||||
const editorIdentifier = this.components.getDefaultEditorIdentifier(this.defaultTag)
|
||||
|
||||
const noteType = noteTypeForEditorIdentifier(editorIdentifier)
|
||||
|
||||
const note = this.application.items.createTemplateItem<NoteContent, SNNote>(
|
||||
const note = this.items.createTemplateItem<NoteContent, SNNote>(
|
||||
ContentType.TYPES.Note,
|
||||
{
|
||||
text: '',
|
||||
@@ -118,8 +139,8 @@ export class NoteViewController implements ItemViewControllerInterface {
|
||||
this.syncController.setItem(this.item)
|
||||
|
||||
if (this.defaultTagUuid) {
|
||||
const tag = this.application.items.findItem(this.defaultTagUuid) as SNTag
|
||||
await this.application.mutator.addTagToNote(note, tag, addTagHierarchy)
|
||||
const tag = this.items.findItem(this.defaultTagUuid) as SNTag
|
||||
await this.mutator.addTagToNote(note, tag, addTagHierarchy)
|
||||
}
|
||||
|
||||
this.notifyObservers(this.item, PayloadEmitSource.InitialObserverRegistrationPush)
|
||||
@@ -140,7 +161,7 @@ export class NoteViewController implements ItemViewControllerInterface {
|
||||
}
|
||||
|
||||
this.disposers.push(
|
||||
this.application.streamItems<SNNote>(ContentType.TYPES.Note, ({ changed, inserted, source }) => {
|
||||
this.items.streamItems<SNNote>(ContentType.TYPES.Note, ({ changed, inserted, source }) => {
|
||||
if (this.dealloced) {
|
||||
return
|
||||
}
|
||||
@@ -163,7 +184,7 @@ export class NoteViewController implements ItemViewControllerInterface {
|
||||
public insertTemplatedNote(): Promise<DecryptedItemInterface> {
|
||||
log(LoggingDomain.NoteView, 'Inserting template note')
|
||||
this.isTemplateNote = false
|
||||
return this.application.mutator.insertItem(this.item)
|
||||
return this.mutator.insertItem(this.item)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -105,7 +105,7 @@ const NoteConflictResolutionModal = ({
|
||||
mutator.conflictOf = undefined
|
||||
})
|
||||
setIsPerformingAction(false)
|
||||
void application.controllers.selectionController.selectItem(selectedNotes[0].uuid, true)
|
||||
void application.itemListController.selectItem(selectedNotes[0].uuid, true)
|
||||
void application.sync.sync()
|
||||
close()
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
*/
|
||||
|
||||
import { WebApplication } from '@/Application/WebApplication'
|
||||
import { ViewControllerManager } from '@/Controllers/ViewControllerManager'
|
||||
import { NotesController } from '@/Controllers/NotesController/NotesController'
|
||||
import {
|
||||
ApplicationEvent,
|
||||
@@ -18,7 +17,7 @@ import { NoteViewController } from './Controller/NoteViewController'
|
||||
describe('NoteView', () => {
|
||||
let noteViewController: NoteViewController
|
||||
let application: WebApplication
|
||||
let viewControllerManager: ViewControllerManager
|
||||
|
||||
let notesController: NotesController
|
||||
|
||||
const createNoteView = () =>
|
||||
@@ -37,13 +36,11 @@ describe('NoteView', () => {
|
||||
notesController.getSpellcheckStateForNote = jest.fn()
|
||||
notesController.getEditorWidthForNote = jest.fn()
|
||||
|
||||
viewControllerManager = {
|
||||
notesController: notesController,
|
||||
} as jest.Mocked<ViewControllerManager>
|
||||
|
||||
application = {
|
||||
controllers: viewControllerManager,
|
||||
} as jest.Mocked<WebApplication>
|
||||
notesController,
|
||||
noteViewController,
|
||||
} as unknown as jest.Mocked<WebApplication>
|
||||
|
||||
application.hasProtectionSources = jest.fn().mockReturnValue(true)
|
||||
application.authorizeNoteAccess = jest.fn()
|
||||
application.addWebEventObserver = jest.fn()
|
||||
|
||||
@@ -111,7 +111,7 @@ class NoteView extends AbstractComponent<NoteViewProps, State> {
|
||||
if (!this.controller || this.controller.dealloced) {
|
||||
return
|
||||
}
|
||||
this.application.getDesktopService()?.redoSearch()
|
||||
this.application.desktopManager?.redoSearch()
|
||||
}
|
||||
|
||||
this.debounceReloadEditorComponent = debounce(this.debounceReloadEditorComponent.bind(this), 25)
|
||||
@@ -215,7 +215,7 @@ class NoteView extends AbstractComponent<NoteViewProps, State> {
|
||||
|
||||
this.autorun(() => {
|
||||
this.setState({
|
||||
showProtectedWarning: this.viewControllerManager.notesController.showProtectedWarning,
|
||||
showProtectedWarning: this.application.notesController.showProtectedWarning,
|
||||
})
|
||||
})
|
||||
|
||||
@@ -224,7 +224,7 @@ class NoteView extends AbstractComponent<NoteViewProps, State> {
|
||||
|
||||
const showProtectedWarning =
|
||||
this.note.protected &&
|
||||
(!this.application.hasProtectionSources() || !this.application.hasUnprotectedAccessSession())
|
||||
(!this.application.hasProtectionSources() || !this.application.protections.hasUnprotectedAccessSession())
|
||||
this.setShowProtectedOverlay(showProtectedWarning)
|
||||
|
||||
this.reloadPreferences().catch(console.error)
|
||||
@@ -429,7 +429,7 @@ class NoteView extends AbstractComponent<NoteViewProps, State> {
|
||||
}
|
||||
|
||||
streamItems() {
|
||||
this.removeNoteStreamObserver = this.application.streamItems<SNNote>(ContentType.TYPES.Note, async () => {
|
||||
this.removeNoteStreamObserver = this.application.items.streamItems<SNNote>(ContentType.TYPES.Note, async () => {
|
||||
if (!this.note) {
|
||||
return
|
||||
}
|
||||
@@ -541,7 +541,7 @@ class NoteView extends AbstractComponent<NoteViewProps, State> {
|
||||
})
|
||||
this.setStatus({
|
||||
type: 'saved',
|
||||
message: 'All changes saved' + (this.application.noAccount() ? ' offline' : ''),
|
||||
message: 'All changes saved' + (this.application.sessions.isSignedOut() ? ' offline' : ''),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -615,7 +615,7 @@ class NoteView extends AbstractComponent<NoteViewProps, State> {
|
||||
}
|
||||
|
||||
setShowProtectedOverlay(show: boolean) {
|
||||
this.viewControllerManager.notesController.setShowProtectedWarning(show)
|
||||
this.application.notesController.setShowProtectedWarning(show)
|
||||
}
|
||||
|
||||
async deleteNote(permanently: boolean) {
|
||||
@@ -677,7 +677,7 @@ class NoteView extends AbstractComponent<NoteViewProps, State> {
|
||||
}
|
||||
|
||||
async reloadSpellcheck() {
|
||||
const spellcheck = this.viewControllerManager.notesController.getSpellcheckStateForNote(this.note)
|
||||
const spellcheck = this.application.notesController.getSpellcheckStateForNote(this.note)
|
||||
if (spellcheck !== this.state.spellcheck) {
|
||||
reloadFont(this.state.monospaceFont)
|
||||
this.setState({ spellcheck })
|
||||
@@ -685,7 +685,7 @@ class NoteView extends AbstractComponent<NoteViewProps, State> {
|
||||
}
|
||||
|
||||
reloadLineWidth() {
|
||||
const editorLineWidth = this.viewControllerManager.notesController.getEditorWidthForNote(this.note)
|
||||
const editorLineWidth = this.application.notesController.getEditorWidthForNote(this.note)
|
||||
|
||||
this.setState({
|
||||
editorLineWidth,
|
||||
@@ -849,15 +849,15 @@ class NoteView extends AbstractComponent<NoteViewProps, State> {
|
||||
{this.note && (
|
||||
<NoteViewFileDropTarget
|
||||
note={this.note}
|
||||
linkingController={this.viewControllerManager.linkingController}
|
||||
filesController={this.viewControllerManager.filesController}
|
||||
linkingController={this.application.linkingController}
|
||||
filesController={this.application.filesController}
|
||||
noteViewElement={this.noteViewElementRef.current}
|
||||
/>
|
||||
)}
|
||||
|
||||
{this.state.noteLocked && (
|
||||
<EditingDisabledBanner
|
||||
onClick={() => this.viewControllerManager.notesController.setLockSelectedNotes(!this.state.noteLocked)}
|
||||
onClick={() => this.application.notesController.setLockSelectedNotes(!this.state.noteLocked)}
|
||||
noteLocked={this.state.noteLocked}
|
||||
/>
|
||||
)}
|
||||
@@ -913,36 +913,26 @@ class NoteView extends AbstractComponent<NoteViewProps, State> {
|
||||
<div className="note-view-options-buttons flex items-center gap-3">
|
||||
<CollaborationInfoHUD item={this.note} />
|
||||
<LinkedItemsButton
|
||||
filesController={this.viewControllerManager.filesController}
|
||||
linkingController={this.viewControllerManager.linkingController}
|
||||
linkingController={this.application.linkingController}
|
||||
onClickPreprocessing={this.ensureNoteIsInsertedBeforeUIAction}
|
||||
featuresController={this.viewControllerManager.featuresController}
|
||||
/>
|
||||
<ChangeEditorButton
|
||||
viewControllerManager={this.viewControllerManager}
|
||||
noteViewController={this.controller}
|
||||
onClickPreprocessing={this.ensureNoteIsInsertedBeforeUIAction}
|
||||
/>
|
||||
<PinNoteButton
|
||||
notesController={this.viewControllerManager.notesController}
|
||||
notesController={this.application.notesController}
|
||||
onClickPreprocessing={this.ensureNoteIsInsertedBeforeUIAction}
|
||||
/>
|
||||
<NotesOptionsPanel
|
||||
navigationController={this.viewControllerManager.navigationController}
|
||||
notesController={this.viewControllerManager.notesController}
|
||||
linkingController={this.viewControllerManager.linkingController}
|
||||
historyModalController={this.viewControllerManager.historyModalController}
|
||||
selectionController={this.viewControllerManager.selectionController}
|
||||
notesController={this.application.notesController}
|
||||
onClickPreprocessing={this.ensureNoteIsInsertedBeforeUIAction}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="hidden md:block">
|
||||
<LinkedItemBubblesContainer
|
||||
item={this.note}
|
||||
linkingController={this.viewControllerManager.linkingController}
|
||||
/>
|
||||
<LinkedItemBubblesContainer item={this.note} linkingController={this.application.linkingController} />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
@@ -990,8 +980,8 @@ class NoteView extends AbstractComponent<NoteViewProps, State> {
|
||||
<SuperEditor
|
||||
key={this.note.uuid}
|
||||
application={this.application}
|
||||
linkingController={this.viewControllerManager.linkingController}
|
||||
filesController={this.viewControllerManager.filesController}
|
||||
linkingController={this.application.linkingController}
|
||||
filesController={this.application.filesController}
|
||||
spellcheck={this.state.spellcheck}
|
||||
controller={this.controller}
|
||||
/>
|
||||
|
||||
@@ -24,7 +24,7 @@ const NoteViewFileDropTarget = ({ note, linkingController, noteViewElement, file
|
||||
tooltipText: 'Drop your files to upload and link them to the current note',
|
||||
callback: async (uploadedFile) => {
|
||||
await linkingController.linkItems(note, uploadedFile)
|
||||
void application.changeAndSaveItem(uploadedFile, (mutator) => {
|
||||
void application.changeAndSaveItem.execute(uploadedFile, (mutator) => {
|
||||
mutator.protected = note.protected
|
||||
})
|
||||
filesController.notifyObserversOfUploadedFileLinkingToCurrentNote(uploadedFile.uuid)
|
||||
|
||||
@@ -46,6 +46,7 @@ export const PlainEditor = forwardRef<PlainEditorInterface, Props>(
|
||||
const note = useRef(controller.item)
|
||||
|
||||
const tabObserverDisposer = useRef<Disposer>()
|
||||
const mutationObserver = useRef<MutationObserver | null>(null)
|
||||
|
||||
useImperativeHandle(ref, () => ({
|
||||
focus() {
|
||||
@@ -53,6 +54,15 @@ export const PlainEditor = forwardRef<PlainEditorInterface, Props>(
|
||||
},
|
||||
}))
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
mutationObserver.current?.disconnect()
|
||||
tabObserverDisposer.current?.()
|
||||
tabObserverDisposer.current = undefined
|
||||
mutationObserver.current = null
|
||||
}
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
const disposer = controller.addNoteInnerValueChangeObserver((updatedNote, source) => {
|
||||
if (updatedNote.uuid !== note.current.uuid) {
|
||||
@@ -219,15 +229,18 @@ export const PlainEditor = forwardRef<PlainEditorInterface, Props>(
|
||||
const observer = new MutationObserver((records) => {
|
||||
for (const record of records) {
|
||||
record.removedNodes.forEach((node) => {
|
||||
if (node === editor) {
|
||||
if (node.isEqualNode(editor)) {
|
||||
tabObserverDisposer.current?.()
|
||||
tabObserverDisposer.current = undefined
|
||||
observer.disconnect()
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
observer.observe(editor.parentElement as HTMLElement, { childList: true })
|
||||
|
||||
mutationObserver.current = observer
|
||||
}
|
||||
|
||||
if (textareaUnloading) {
|
||||
|
||||
@@ -1,30 +1,14 @@
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import NotesOptions from '@/Components/NotesOptions/NotesOptions'
|
||||
import { useCallback, useState } from 'react'
|
||||
import { NotesController } from '@/Controllers/NotesController/NotesController'
|
||||
import { NavigationController } from '@/Controllers/Navigation/NavigationController'
|
||||
import { HistoryModalController } from '@/Controllers/NoteHistory/HistoryModalController'
|
||||
import Popover from '../Popover/Popover'
|
||||
import { LinkingController } from '@/Controllers/LinkingController'
|
||||
import Menu from '../Menu/Menu'
|
||||
import { SelectedItemsController } from '@/Controllers/SelectedItemsController'
|
||||
import { useApplication } from '../ApplicationProvider'
|
||||
|
||||
type Props = {
|
||||
navigationController: NavigationController
|
||||
notesController: NotesController
|
||||
linkingController: LinkingController
|
||||
historyModalController: HistoryModalController
|
||||
selectionController: SelectedItemsController
|
||||
}
|
||||
const NotesContextMenu = () => {
|
||||
const application = useApplication()
|
||||
|
||||
const NotesContextMenu = ({
|
||||
navigationController,
|
||||
notesController,
|
||||
linkingController,
|
||||
historyModalController,
|
||||
selectionController,
|
||||
}: Props) => {
|
||||
const { contextMenuOpen, contextMenuClickLocation, setContextMenuOpen } = notesController
|
||||
const { contextMenuOpen, contextMenuClickLocation, setContextMenuOpen } = application.notesController
|
||||
|
||||
const closeMenu = () => setContextMenuOpen(!contextMenuOpen)
|
||||
|
||||
@@ -48,12 +32,7 @@ const NotesContextMenu = ({
|
||||
>
|
||||
<Menu className="select-none" a11yLabel="Note context menu" isOpen={contextMenuOpen}>
|
||||
<NotesOptions
|
||||
notes={notesController.selectedNotes}
|
||||
navigationController={navigationController}
|
||||
notesController={notesController}
|
||||
linkingController={linkingController}
|
||||
historyModalController={historyModalController}
|
||||
selectionController={selectionController}
|
||||
notes={application.notesController.selectedNotes}
|
||||
requestDisableClickOutside={handleDisableClickOutsideRequest}
|
||||
closeMenu={closeMenu}
|
||||
/>
|
||||
|
||||
@@ -50,15 +50,7 @@ const iconClassDanger = `text-danger mr-2 ${iconSize}`
|
||||
const iconClassWarning = `text-warning mr-2 ${iconSize}`
|
||||
const iconClassSuccess = `text-success mr-2 ${iconSize}`
|
||||
|
||||
const NotesOptions = ({
|
||||
notes,
|
||||
navigationController,
|
||||
notesController,
|
||||
linkingController,
|
||||
selectionController,
|
||||
historyModalController,
|
||||
closeMenu,
|
||||
}: NotesOptionsProps) => {
|
||||
const NotesOptions = ({ notes, closeMenu }: NotesOptionsProps) => {
|
||||
const application = useApplication()
|
||||
|
||||
const [altKeyDown, setAltKeyDown] = useState(false)
|
||||
@@ -117,7 +109,7 @@ const NotesOptions = ({
|
||||
if (notes.length === 1) {
|
||||
const note = notes[0]
|
||||
const blob = getNoteBlob(application, note)
|
||||
application.getArchiveService().downloadData(blob, getNoteFileName(application, note))
|
||||
application.archiveService.downloadData(blob, getNoteFileName(application, note))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -126,7 +118,7 @@ const NotesOptions = ({
|
||||
type: ToastType.Loading,
|
||||
message: `Exporting ${notes.length} notes...`,
|
||||
})
|
||||
await application.getArchiveService().downloadDataAsZip(
|
||||
await application.archiveService.downloadDataAsZip(
|
||||
notes.map((note) => {
|
||||
return {
|
||||
name: getNoteFileName(application, note),
|
||||
@@ -163,7 +155,7 @@ const NotesOptions = ({
|
||||
{
|
||||
label: 'Open',
|
||||
handler: (toastId) => {
|
||||
selectionController.selectItem(duplicated.uuid, true).catch(console.error)
|
||||
application.itemListController.selectItem(duplicated.uuid, true).catch(console.error)
|
||||
dismissToast(toastId)
|
||||
},
|
||||
},
|
||||
@@ -176,11 +168,11 @@ const NotesOptions = ({
|
||||
)
|
||||
void application.sync.sync()
|
||||
closeMenuAndToggleNotesList()
|
||||
}, [application.mutator, application.sync, closeMenuAndToggleNotesList, notes, selectionController])
|
||||
}, [application.mutator, application.itemListController, application.sync, closeMenuAndToggleNotesList, notes])
|
||||
|
||||
const openRevisionHistoryModal = useCallback(() => {
|
||||
historyModalController.openModal(notesController.firstSelectedNote)
|
||||
}, [historyModalController, notesController.firstSelectedNote])
|
||||
application.historyModalController.openModal(application.notesController.firstSelectedNote)
|
||||
}, [application.historyModalController, application.notesController.firstSelectedNote])
|
||||
|
||||
const historyShortcut = useMemo(
|
||||
() => application.keyboardService.keyboardShortcutForCommand(OPEN_NOTE_HISTORY_COMMAND),
|
||||
@@ -243,7 +235,7 @@ const NotesOptions = ({
|
||||
<MenuSwitchButtonItem
|
||||
checked={locked}
|
||||
onChange={(locked) => {
|
||||
notesController.setLockSelectedNotes(locked)
|
||||
application.notesController.setLockSelectedNotes(locked)
|
||||
}}
|
||||
>
|
||||
<Icon type="pencil-off" className={iconClass} />
|
||||
@@ -252,7 +244,7 @@ const NotesOptions = ({
|
||||
<MenuSwitchButtonItem
|
||||
checked={!hidePreviews}
|
||||
onChange={(hidePreviews) => {
|
||||
notesController.setHideSelectedNotePreviews(!hidePreviews)
|
||||
application.notesController.setHideSelectedNotePreviews(!hidePreviews)
|
||||
}}
|
||||
>
|
||||
<Icon type="rich-text" className={iconClass} />
|
||||
@@ -261,7 +253,7 @@ const NotesOptions = ({
|
||||
<MenuSwitchButtonItem
|
||||
checked={protect}
|
||||
onChange={(protect) => {
|
||||
notesController.setProtectSelectedNotes(protect).catch(console.error)
|
||||
application.notesController.setProtectSelectedNotes(protect).catch(console.error)
|
||||
}}
|
||||
>
|
||||
<Icon type="lock" className={iconClass} />
|
||||
@@ -277,17 +269,17 @@ const NotesOptions = ({
|
||||
|
||||
{featureTrunkVaultsEnabled() && <AddToVaultMenuOption iconClassName={iconClass} items={notes} />}
|
||||
|
||||
{navigationController.tagsCount > 0 && (
|
||||
{application.navigationController.tagsCount > 0 && (
|
||||
<AddTagOption
|
||||
iconClassName={iconClass}
|
||||
navigationController={navigationController}
|
||||
navigationController={application.navigationController}
|
||||
selectedItems={notes}
|
||||
linkingController={linkingController}
|
||||
linkingController={application.linkingController}
|
||||
/>
|
||||
)}
|
||||
<MenuItem
|
||||
onClick={() => {
|
||||
notesController.setStarSelectedNotes(!starred)
|
||||
application.notesController.setStarSelectedNotes(!starred)
|
||||
}}
|
||||
>
|
||||
<Icon type="star" className={iconClass} />
|
||||
@@ -298,7 +290,7 @@ const NotesOptions = ({
|
||||
{unpinned && (
|
||||
<MenuItem
|
||||
onClick={() => {
|
||||
notesController.setPinSelectedNotes(true)
|
||||
application.notesController.setPinSelectedNotes(true)
|
||||
}}
|
||||
>
|
||||
<Icon type="pin" className={iconClass} />
|
||||
@@ -309,7 +301,7 @@ const NotesOptions = ({
|
||||
{pinned && (
|
||||
<MenuItem
|
||||
onClick={() => {
|
||||
notesController.setPinSelectedNotes(false)
|
||||
application.notesController.setPinSelectedNotes(false)
|
||||
}}
|
||||
>
|
||||
<Icon type="unpin" className={iconClass} />
|
||||
@@ -394,7 +386,7 @@ const NotesOptions = ({
|
||||
{unarchived && (
|
||||
<MenuItem
|
||||
onClick={async () => {
|
||||
await notesController.setArchiveSelectedNotes(true).catch(console.error)
|
||||
await application.notesController.setArchiveSelectedNotes(true).catch(console.error)
|
||||
closeMenuAndToggleNotesList()
|
||||
}}
|
||||
>
|
||||
@@ -405,7 +397,7 @@ const NotesOptions = ({
|
||||
{archived && (
|
||||
<MenuItem
|
||||
onClick={async () => {
|
||||
await notesController.setArchiveSelectedNotes(false).catch(console.error)
|
||||
await application.notesController.setArchiveSelectedNotes(false).catch(console.error)
|
||||
closeMenuAndToggleNotesList()
|
||||
}}
|
||||
>
|
||||
@@ -417,14 +409,14 @@ const NotesOptions = ({
|
||||
(altKeyDown ? (
|
||||
<DeletePermanentlyButton
|
||||
onClick={async () => {
|
||||
await notesController.deleteNotesPermanently()
|
||||
await application.notesController.deleteNotesPermanently()
|
||||
closeMenuAndToggleNotesList()
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<MenuItem
|
||||
onClick={async () => {
|
||||
await notesController.setTrashSelectedNotes(true)
|
||||
await application.notesController.setTrashSelectedNotes(true)
|
||||
closeMenuAndToggleNotesList()
|
||||
}}
|
||||
>
|
||||
@@ -436,7 +428,7 @@ const NotesOptions = ({
|
||||
<>
|
||||
<MenuItem
|
||||
onClick={async () => {
|
||||
await notesController.setTrashSelectedNotes(false)
|
||||
await application.notesController.setTrashSelectedNotes(false)
|
||||
closeMenuAndToggleNotesList()
|
||||
}}
|
||||
>
|
||||
@@ -445,13 +437,13 @@ const NotesOptions = ({
|
||||
</MenuItem>
|
||||
<DeletePermanentlyButton
|
||||
onClick={async () => {
|
||||
await notesController.deleteNotesPermanently()
|
||||
await application.notesController.deleteNotesPermanently()
|
||||
closeMenuAndToggleNotesList()
|
||||
}}
|
||||
/>
|
||||
<MenuItem
|
||||
onClick={async () => {
|
||||
await notesController.emptyTrash()
|
||||
await application.notesController.emptyTrash()
|
||||
closeMenuAndToggleNotesList()
|
||||
}}
|
||||
>
|
||||
@@ -459,7 +451,7 @@ const NotesOptions = ({
|
||||
<Icon type="trash-sweep" className="mr-2 text-danger" />
|
||||
<div className="flex-row">
|
||||
<div className="text-danger">Empty Trash</div>
|
||||
<div className="text-xs">{notesController.trashedNotesCount} notes in Trash</div>
|
||||
<div className="text-xs">{application.notesController.trashedNotesCount} notes in Trash</div>
|
||||
</div>
|
||||
</div>
|
||||
</MenuItem>
|
||||
@@ -482,7 +474,11 @@ const NotesOptions = ({
|
||||
<HorizontalSeparator classes="my-2" />
|
||||
|
||||
{editorForNote && (
|
||||
<SpellcheckOptions editorForNote={editorForNote} notesController={notesController} note={notes[0]} />
|
||||
<SpellcheckOptions
|
||||
editorForNote={editorForNote}
|
||||
notesController={application.notesController}
|
||||
note={notes[0]}
|
||||
/>
|
||||
)}
|
||||
|
||||
<HorizontalSeparator classes="my-2" />
|
||||
|
||||
@@ -2,31 +2,16 @@ import { useCallback, useRef, useState } from 'react'
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import NotesOptions from './NotesOptions'
|
||||
import { NotesController } from '@/Controllers/NotesController/NotesController'
|
||||
import { NavigationController } from '@/Controllers/Navigation/NavigationController'
|
||||
import { HistoryModalController } from '@/Controllers/NoteHistory/HistoryModalController'
|
||||
import Popover from '../Popover/Popover'
|
||||
import { LinkingController } from '@/Controllers/LinkingController'
|
||||
import RoundIconButton from '../Button/RoundIconButton'
|
||||
import Menu from '../Menu/Menu'
|
||||
import { SelectedItemsController } from '@/Controllers/SelectedItemsController'
|
||||
|
||||
type Props = {
|
||||
navigationController: NavigationController
|
||||
notesController: NotesController
|
||||
linkingController: LinkingController
|
||||
historyModalController: HistoryModalController
|
||||
selectionController: SelectedItemsController
|
||||
onClickPreprocessing?: () => Promise<void>
|
||||
}
|
||||
|
||||
const NotesOptionsPanel = ({
|
||||
navigationController,
|
||||
notesController,
|
||||
linkingController,
|
||||
historyModalController,
|
||||
selectionController,
|
||||
onClickPreprocessing,
|
||||
}: Props) => {
|
||||
const NotesOptionsPanel = ({ notesController, onClickPreprocessing }: Props) => {
|
||||
const [isOpen, setIsOpen] = useState(false)
|
||||
const buttonRef = useRef<HTMLButtonElement>(null)
|
||||
|
||||
@@ -57,11 +42,6 @@ const NotesOptionsPanel = ({
|
||||
<Menu a11yLabel="Note options menu" isOpen={isOpen}>
|
||||
<NotesOptions
|
||||
notes={notesController.selectedNotes}
|
||||
navigationController={navigationController}
|
||||
notesController={notesController}
|
||||
linkingController={linkingController}
|
||||
historyModalController={historyModalController}
|
||||
selectionController={selectionController}
|
||||
requestDisableClickOutside={handleDisableClickOutsideRequest}
|
||||
closeMenu={toggleMenu}
|
||||
/>
|
||||
|
||||
@@ -1,17 +1,7 @@
|
||||
import { HistoryModalController } from '@/Controllers/NoteHistory/HistoryModalController'
|
||||
import { NavigationController } from '@/Controllers/Navigation/NavigationController'
|
||||
import { NotesController } from '@/Controllers/NotesController/NotesController'
|
||||
import { LinkingController } from '@/Controllers/LinkingController'
|
||||
import { SNNote } from '@standardnotes/snjs'
|
||||
import { SelectedItemsController } from '@/Controllers/SelectedItemsController'
|
||||
|
||||
export type NotesOptionsProps = {
|
||||
notes: SNNote[]
|
||||
navigationController: NavigationController
|
||||
notesController: NotesController
|
||||
linkingController: LinkingController
|
||||
historyModalController: HistoryModalController
|
||||
selectionController: SelectedItemsController
|
||||
requestDisableClickOutside?: (disabled: boolean) => void
|
||||
closeMenu: () => void
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { useCallback, useRef } from 'react'
|
||||
import { WebApplication } from '@/Application/WebApplication'
|
||||
import { ViewControllerManager } from '@/Controllers/ViewControllerManager'
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import Button from '@/Components/Button/Button'
|
||||
import Icon from '../Icon/Icon'
|
||||
@@ -8,15 +7,14 @@ import AlertDialog from '../AlertDialog/AlertDialog'
|
||||
|
||||
type Props = {
|
||||
application: WebApplication
|
||||
viewControllerManager: ViewControllerManager
|
||||
}
|
||||
|
||||
const ConfirmOtherSessionsSignOut = observer(({ application, viewControllerManager }: Props) => {
|
||||
const ConfirmOtherSessionsSignOut = observer(({ application }: Props) => {
|
||||
const cancelRef = useRef<HTMLButtonElement>(null)
|
||||
|
||||
const closeDialog = useCallback(() => {
|
||||
viewControllerManager.accountMenuController.setOtherSessionsSignOut(false)
|
||||
}, [viewControllerManager])
|
||||
application.accountMenuController.setOtherSessionsSignOut(false)
|
||||
}, [application])
|
||||
|
||||
const confirm = useCallback(() => {
|
||||
application.revokeAllOtherSessions().catch(console.error)
|
||||
@@ -55,7 +53,7 @@ const ConfirmOtherSessionsSignOut = observer(({ application, viewControllerManag
|
||||
ConfirmOtherSessionsSignOut.displayName = 'ConfirmOtherSessionsSignOut'
|
||||
|
||||
const OtherSessionsSignOutContainer = (props: Props) => {
|
||||
if (!props.viewControllerManager.accountMenuController.otherSessionsSignOut) {
|
||||
if (!props.application.accountMenuController.otherSessionsSignOut) {
|
||||
return null
|
||||
}
|
||||
return <ConfirmOtherSessionsSignOut {...props} />
|
||||
|
||||
@@ -38,8 +38,6 @@ const PanesSystemComponent = () => {
|
||||
const [panesPendingEntrance, setPanesPendingEntrance] = useState<AppPaneId[]>([])
|
||||
const [panesPendingExit, setPanesPendingExit] = useState<AppPaneId[]>([])
|
||||
|
||||
const viewControllerManager = application.controllers
|
||||
|
||||
const [navigationPanelWidth, setNavigationPanelWidth] = useState<number>(
|
||||
application.getPreference(PrefKey.TagsPanelWidth, PLACEHOLDER_NAVIGATION_PANEL_WIDTH),
|
||||
)
|
||||
@@ -296,18 +294,6 @@ const PanesSystemComponent = () => {
|
||||
key={'content-list-view'}
|
||||
application={application}
|
||||
onPanelWidthLoad={handleInitialItemsListPanelWidthLoad}
|
||||
accountMenuController={viewControllerManager.accountMenuController}
|
||||
filesController={viewControllerManager.filesController}
|
||||
itemListController={viewControllerManager.itemListController}
|
||||
navigationController={viewControllerManager.navigationController}
|
||||
noAccountWarningController={viewControllerManager.noAccountWarningController}
|
||||
notesController={viewControllerManager.notesController}
|
||||
selectionController={viewControllerManager.selectionController}
|
||||
searchOptionsController={viewControllerManager.searchOptionsController}
|
||||
linkingController={viewControllerManager.linkingController}
|
||||
featuresController={viewControllerManager.featuresController}
|
||||
historyModalController={viewControllerManager.historyModalController}
|
||||
paneController={viewControllerManager.paneController}
|
||||
>
|
||||
{showPanelResizers && listRef && (
|
||||
<PanelResizer
|
||||
|
||||
@@ -153,7 +153,7 @@ class PasswordWizard extends AbstractComponent<Props, State> {
|
||||
return false
|
||||
}
|
||||
|
||||
if (!this.application.getUser()?.email) {
|
||||
if (!this.application.sessions.getUser()?.email) {
|
||||
this.application.alerts
|
||||
.alert("We don't have your email stored. Please sign out then log back in to fix this issue.")
|
||||
.catch(console.error)
|
||||
|
||||
@@ -15,39 +15,23 @@ import Vaults from './Panes/Vaults/Vaults'
|
||||
|
||||
const PaneSelector: FunctionComponent<PreferencesProps & { menu: PreferencesSessionController }> = ({
|
||||
menu,
|
||||
viewControllerManager,
|
||||
application,
|
||||
mfaProvider,
|
||||
userProvider,
|
||||
}) => {
|
||||
switch (menu.selectedPaneId) {
|
||||
case 'general':
|
||||
return (
|
||||
<General
|
||||
viewControllerManager={viewControllerManager}
|
||||
application={application}
|
||||
extensionsLatestVersions={menu.extensionsLatestVersions}
|
||||
/>
|
||||
)
|
||||
return <General application={application} extensionsLatestVersions={menu.extensionsLatestVersions} />
|
||||
case 'account':
|
||||
return <AccountPreferences application={application} viewControllerManager={viewControllerManager} />
|
||||
return <AccountPreferences application={application} />
|
||||
case 'appearance':
|
||||
return <Appearance application={application} />
|
||||
case 'home-server':
|
||||
return <HomeServer />
|
||||
case 'security':
|
||||
return (
|
||||
<Security
|
||||
mfaProvider={mfaProvider}
|
||||
userProvider={userProvider}
|
||||
viewControllerManager={viewControllerManager}
|
||||
application={application}
|
||||
/>
|
||||
)
|
||||
return <Security application={application} />
|
||||
case 'vaults':
|
||||
return <Vaults />
|
||||
case 'backups':
|
||||
return <Backups application={application} viewControllerManager={viewControllerManager} />
|
||||
return <Backups application={application} />
|
||||
case 'listed':
|
||||
return <Listed application={application} />
|
||||
case 'shortcuts':
|
||||
@@ -61,13 +45,7 @@ const PaneSelector: FunctionComponent<PreferencesProps & { menu: PreferencesSess
|
||||
case 'whats-new':
|
||||
return <WhatsNew application={application} />
|
||||
default:
|
||||
return (
|
||||
<General
|
||||
viewControllerManager={viewControllerManager}
|
||||
application={application}
|
||||
extensionsLatestVersions={menu.extensionsLatestVersions}
|
||||
/>
|
||||
)
|
||||
return <General application={application} extensionsLatestVersions={menu.extensionsLatestVersions} />
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { observer } from 'mobx-react-lite'
|
||||
|
||||
import { WebApplication } from '@/Application/WebApplication'
|
||||
import { ViewControllerManager } from '@/Controllers/ViewControllerManager'
|
||||
import Authentication from './Authentication'
|
||||
import Credentials from './Credentials'
|
||||
import Sync from './Sync'
|
||||
@@ -15,30 +14,29 @@ import DeleteAccount from '@/Components/Preferences/Panes/Account/DeleteAccount'
|
||||
|
||||
type Props = {
|
||||
application: WebApplication
|
||||
viewControllerManager: ViewControllerManager
|
||||
}
|
||||
|
||||
const AccountPreferences = ({ application, viewControllerManager }: Props) => {
|
||||
const AccountPreferences = ({ application }: Props) => {
|
||||
const isUsingThirdPartyServer = application.isThirdPartyHostUsed()
|
||||
|
||||
return (
|
||||
<PreferencesPane>
|
||||
{!application.hasAccount() ? (
|
||||
<Authentication application={application} viewControllerManager={viewControllerManager} />
|
||||
<Authentication application={application} />
|
||||
) : (
|
||||
<>
|
||||
<Credentials application={application} viewControllerManager={viewControllerManager} />
|
||||
<Credentials application={application} />
|
||||
<Sync application={application} />
|
||||
</>
|
||||
)}
|
||||
<Subscription />
|
||||
<SubscriptionSharing application={application} viewControllerManager={viewControllerManager} />
|
||||
{application.hasAccount() && viewControllerManager.featuresController.entitledToFiles && (
|
||||
<SubscriptionSharing application={application} />
|
||||
{application.hasAccount() && application.featuresController.entitledToFiles && (
|
||||
<FilesSection application={application} />
|
||||
)}
|
||||
{application.hasAccount() && !isUsingThirdPartyServer && <Email application={application} />}
|
||||
<SignOutWrapper application={application} viewControllerManager={viewControllerManager} />
|
||||
<DeleteAccount application={application} viewControllerManager={viewControllerManager} />
|
||||
<SignOutWrapper application={application} />
|
||||
<DeleteAccount application={application} />
|
||||
</PreferencesPane>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import Button from '@/Components/Button/Button'
|
||||
import { Title } from '@/Components/Preferences/PreferencesComponents/Content'
|
||||
import { WebApplication } from '@/Application/WebApplication'
|
||||
import { ViewControllerManager } from '@/Controllers/ViewControllerManager'
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import { FunctionComponent } from 'react'
|
||||
import { AccountIllustration } from '@standardnotes/icons'
|
||||
@@ -11,20 +10,19 @@ import PreferencesSegment from '../../PreferencesComponents/PreferencesSegment'
|
||||
|
||||
type Props = {
|
||||
application: WebApplication
|
||||
viewControllerManager: ViewControllerManager
|
||||
}
|
||||
|
||||
const Authentication: FunctionComponent<Props> = ({ viewControllerManager }) => {
|
||||
const Authentication: FunctionComponent<Props> = ({ application }) => {
|
||||
const clickSignIn = () => {
|
||||
viewControllerManager.preferencesController.closePreferences()
|
||||
viewControllerManager.accountMenuController.setCurrentPane(AccountMenuPane.SignIn)
|
||||
viewControllerManager.accountMenuController.setShow(true)
|
||||
application.preferencesController.closePreferences()
|
||||
application.accountMenuController.setCurrentPane(AccountMenuPane.SignIn)
|
||||
application.accountMenuController.setShow(true)
|
||||
}
|
||||
|
||||
const clickRegister = () => {
|
||||
viewControllerManager.preferencesController.closePreferences()
|
||||
viewControllerManager.accountMenuController.setCurrentPane(AccountMenuPane.Register)
|
||||
viewControllerManager.accountMenuController.setShow(true)
|
||||
application.preferencesController.closePreferences()
|
||||
application.accountMenuController.setCurrentPane(AccountMenuPane.Register)
|
||||
application.accountMenuController.setShow(true)
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import Button from '@/Components/Button/Button'
|
||||
import { ViewControllerManager } from '@/Controllers/ViewControllerManager'
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import { FunctionComponent } from 'react'
|
||||
import { Title, Text } from '../../PreferencesComponents/Content'
|
||||
import PreferencesGroup from '../../PreferencesComponents/PreferencesGroup'
|
||||
import PreferencesSegment from '../../PreferencesComponents/PreferencesSegment'
|
||||
import { useApplication } from '@/Components/ApplicationProvider'
|
||||
|
||||
const ClearSessionDataView: FunctionComponent = () => {
|
||||
const application = useApplication()
|
||||
|
||||
const ClearSessionDataView: FunctionComponent<{
|
||||
viewControllerManager: ViewControllerManager
|
||||
}> = ({ viewControllerManager }) => {
|
||||
return (
|
||||
<PreferencesGroup>
|
||||
<PreferencesSegment>
|
||||
@@ -18,7 +18,7 @@ const ClearSessionDataView: FunctionComponent<{
|
||||
colorStyle="danger"
|
||||
label="Clear workspace"
|
||||
onClick={() => {
|
||||
viewControllerManager.accountMenuController.setSigningOut(true)
|
||||
application.accountMenuController.setSigningOut(true)
|
||||
}}
|
||||
/>
|
||||
</PreferencesSegment>
|
||||
|
||||
@@ -6,7 +6,6 @@ import HorizontalSeparator from '@/Components/Shared/HorizontalSeparator'
|
||||
import { dateToLocalizedString } from '@standardnotes/snjs'
|
||||
import { useCallback, useState, FunctionComponent } from 'react'
|
||||
import ChangeEmail from '@/Components/Preferences/Panes/Account/ChangeEmail/ChangeEmail'
|
||||
import { ViewControllerManager } from '@/Controllers/ViewControllerManager'
|
||||
import PasswordWizard from '@/Components/PasswordWizard/PasswordWizard'
|
||||
import PreferencesGroup from '../../PreferencesComponents/PreferencesGroup'
|
||||
import PreferencesSegment from '../../PreferencesComponents/PreferencesSegment'
|
||||
@@ -14,14 +13,13 @@ import ModalOverlay from '@/Components/Modal/ModalOverlay'
|
||||
|
||||
type Props = {
|
||||
application: WebApplication
|
||||
viewControllerManager: ViewControllerManager
|
||||
}
|
||||
|
||||
const Credentials: FunctionComponent<Props> = ({ application }: Props) => {
|
||||
const [isChangeEmailDialogOpen, setIsChangeEmailDialogOpen] = useState(false)
|
||||
const [shouldShowPasswordWizard, setShouldShowPasswordWizard] = useState(false)
|
||||
|
||||
const user = application.getUser()
|
||||
const user = application.sessions.getUser()
|
||||
|
||||
const passwordCreatedAtTimestamp = application.getUserPasswordCreationDate() as Date
|
||||
const passwordCreatedOn = dateToLocalizedString(passwordCreatedAtTimestamp)
|
||||
|
||||
@@ -3,15 +3,13 @@ import PreferencesSegment from '@/Components/Preferences/PreferencesComponents/P
|
||||
import { Text, Title } from '@/Components/Preferences/PreferencesComponents/Content'
|
||||
import Button from '@/Components/Button/Button'
|
||||
import PreferencesGroup from '@/Components/Preferences/PreferencesComponents/PreferencesGroup'
|
||||
import { ViewControllerManager } from '@Controllers/ViewControllerManager'
|
||||
import { WebApplication } from '@/Application/WebApplication'
|
||||
|
||||
type Props = {
|
||||
application: WebApplication
|
||||
viewControllerManager: ViewControllerManager
|
||||
}
|
||||
|
||||
const DeleteAccount = ({ application, viewControllerManager }: Props) => {
|
||||
const DeleteAccount = ({ application }: Props) => {
|
||||
if (!application.hasAccount()) {
|
||||
return null
|
||||
}
|
||||
@@ -25,7 +23,7 @@ const DeleteAccount = ({ application, viewControllerManager }: Props) => {
|
||||
colorStyle="danger"
|
||||
label="Delete my account"
|
||||
onClick={() => {
|
||||
viewControllerManager.accountMenuController.setDeletingAccount(true)
|
||||
application.accountMenuController.setDeletingAccount(true)
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -43,7 +43,7 @@ const Email: FunctionComponent<Props> = ({ application }: Props) => {
|
||||
}
|
||||
|
||||
const loadSettings = useCallback(async () => {
|
||||
if (!application.getUser()) {
|
||||
if (!application.sessions.getUser()) {
|
||||
return
|
||||
}
|
||||
setIsLoading(true)
|
||||
|
||||
@@ -2,7 +2,6 @@ import Button from '@/Components/Button/Button'
|
||||
import OtherSessionsSignOutContainer from '@/Components/OtherSessionsSignOut/OtherSessionsSignOut'
|
||||
import HorizontalSeparator from '@/Components/Shared/HorizontalSeparator'
|
||||
import { WebApplication } from '@/Application/WebApplication'
|
||||
import { ViewControllerManager } from '@/Controllers/ViewControllerManager'
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import { FunctionComponent } from 'react'
|
||||
import { Subtitle, Title, Text } from '../../PreferencesComponents/Content'
|
||||
@@ -12,10 +11,9 @@ import ClearSessionDataView from './ClearSessionDataView'
|
||||
|
||||
type Props = {
|
||||
application: WebApplication
|
||||
viewControllerManager: ViewControllerManager
|
||||
}
|
||||
|
||||
const SignOutView: FunctionComponent<Props> = observer(({ application, viewControllerManager }) => {
|
||||
const SignOutView: FunctionComponent<Props> = observer(({ application }) => {
|
||||
return (
|
||||
<>
|
||||
<PreferencesGroup>
|
||||
@@ -27,10 +25,10 @@ const SignOutView: FunctionComponent<Props> = observer(({ application, viewContr
|
||||
<Button
|
||||
label="Sign out other sessions"
|
||||
onClick={() => {
|
||||
viewControllerManager.accountMenuController.setOtherSessionsSignOut(true)
|
||||
application.accountMenuController.setOtherSessionsSignOut(true)
|
||||
}}
|
||||
/>
|
||||
<Button label="Manage sessions" onClick={() => viewControllerManager.openSessionsModal()} />
|
||||
<Button label="Manage sessions" onClick={() => application.openSessionsModal()} />
|
||||
</div>
|
||||
</PreferencesSegment>
|
||||
<HorizontalSeparator classes="my-4" />
|
||||
@@ -42,23 +40,23 @@ const SignOutView: FunctionComponent<Props> = observer(({ application, viewContr
|
||||
colorStyle="danger"
|
||||
label="Sign out workspace"
|
||||
onClick={() => {
|
||||
viewControllerManager.accountMenuController.setSigningOut(true)
|
||||
application.accountMenuController.setSigningOut(true)
|
||||
}}
|
||||
/>
|
||||
</PreferencesSegment>
|
||||
</PreferencesGroup>
|
||||
<OtherSessionsSignOutContainer viewControllerManager={viewControllerManager} application={application} />
|
||||
<OtherSessionsSignOutContainer application={application} />
|
||||
</>
|
||||
)
|
||||
})
|
||||
|
||||
SignOutView.displayName = 'SignOutView'
|
||||
|
||||
const SignOutWrapper: FunctionComponent<Props> = ({ application, viewControllerManager }) => {
|
||||
const SignOutWrapper: FunctionComponent<Props> = ({ application }) => {
|
||||
if (!application.hasAccount()) {
|
||||
return <ClearSessionDataView viewControllerManager={viewControllerManager} />
|
||||
return <ClearSessionDataView />
|
||||
}
|
||||
return <SignOutView viewControllerManager={viewControllerManager} application={application} />
|
||||
return <SignOutView application={application} />
|
||||
}
|
||||
|
||||
export default observer(SignOutWrapper)
|
||||
|
||||
@@ -12,16 +12,16 @@ const Subscription: FunctionComponent = () => {
|
||||
const application = useApplication()
|
||||
|
||||
const [onlineSubscription, setOnlineSubscription] = useState<Subscription | undefined>(
|
||||
application.controllers.subscriptionController.onlineSubscription,
|
||||
application.subscriptionController.onlineSubscription,
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
return application.subscriptions.addEventObserver((event) => {
|
||||
if (event === SubscriptionManagerEvent.DidFetchSubscription) {
|
||||
setOnlineSubscription(application.controllers.subscriptionController.onlineSubscription)
|
||||
setOnlineSubscription(application.subscriptionController.onlineSubscription)
|
||||
}
|
||||
})
|
||||
}, [application.subscriptions, application.controllers.subscriptionController])
|
||||
}, [application.subscriptions, application.subscriptionController])
|
||||
|
||||
useEffect(() => {
|
||||
void application.subscriptions.fetchOnlineSubscription()
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import Button from '@/Components/Button/Button'
|
||||
import { openSubscriptionDashboard } from '@/Utils/ManageSubscription'
|
||||
import SubscriptionStatusText from './SubscriptionStatusText'
|
||||
import { useApplication } from '@/Components/ApplicationProvider'
|
||||
|
||||
@@ -8,7 +7,7 @@ const SubscriptionInformation = () => {
|
||||
const application = useApplication()
|
||||
|
||||
const manageSubscription = async () => {
|
||||
void openSubscriptionDashboard(application)
|
||||
void application.openSubscriptionDashboard.execute()
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
import { FeatureStatus, NativeFeatureIdentifier } from '@standardnotes/snjs'
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import { FunctionComponent, useState } from 'react'
|
||||
|
||||
import { Title } from '@/Components/Preferences/PreferencesComponents/Content'
|
||||
import { WebApplication } from '@/Application/WebApplication'
|
||||
import { ViewControllerManager } from '@/Controllers/ViewControllerManager'
|
||||
import PreferencesGroup from '@/Components/Preferences/PreferencesComponents/PreferencesGroup'
|
||||
import PreferencesSegment from '@/Components/Preferences/PreferencesComponents/PreferencesSegment'
|
||||
import HorizontalSeparator from '@/Components/Shared/HorizontalSeparator'
|
||||
@@ -18,13 +16,12 @@ import ModalOverlay from '@/Components/Modal/ModalOverlay'
|
||||
|
||||
type Props = {
|
||||
application: WebApplication
|
||||
viewControllerManager: ViewControllerManager
|
||||
}
|
||||
|
||||
const SubscriptionSharing: FunctionComponent<Props> = ({ application, viewControllerManager }: Props) => {
|
||||
const SubscriptionSharing: FunctionComponent<Props> = ({ application }: Props) => {
|
||||
const [isInviteDialogOpen, setIsInviteDialogOpen] = useState(false)
|
||||
|
||||
const subscriptionState = viewControllerManager.subscriptionController
|
||||
const subscriptionState = application.subscriptionController
|
||||
|
||||
const isReadOnlySession = application.sessions.isCurrentSessionReadOnly()
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { WebApplication } from '@/Application/WebApplication'
|
||||
import { ViewControllerManager } from '@/Controllers/ViewControllerManager'
|
||||
import { FunctionComponent } from 'react'
|
||||
import PreferencesPane from '@/Components/Preferences/PreferencesComponents/PreferencesPane'
|
||||
import DataBackups from './DataBackups'
|
||||
@@ -10,16 +9,15 @@ import TextBackupsCrossPlatform from './TextBackups/TextBackupsCrossPlatform'
|
||||
import PlaintextBackupsCrossPlatform from './PlaintextBackups/PlaintextBackupsCrossPlatform'
|
||||
|
||||
type Props = {
|
||||
viewControllerManager: ViewControllerManager
|
||||
application: WebApplication
|
||||
}
|
||||
|
||||
const Backups: FunctionComponent<Props> = ({ application, viewControllerManager }) => {
|
||||
const Backups: FunctionComponent<Props> = ({ application }) => {
|
||||
const isUsingThirdPartyServer = application.isThirdPartyHostUsed()
|
||||
|
||||
return (
|
||||
<PreferencesPane>
|
||||
<DataBackups application={application} viewControllerManager={viewControllerManager} />
|
||||
<DataBackups application={application} />
|
||||
<TextBackupsCrossPlatform application={application} />
|
||||
<PlaintextBackupsCrossPlatform />
|
||||
<FileBackupsCrossPlatform application={application} />
|
||||
|
||||
@@ -12,7 +12,6 @@ import {
|
||||
import { BackupFile } from '@standardnotes/snjs'
|
||||
import { ChangeEventHandler, MouseEventHandler, useCallback, useEffect, useRef, useState } from 'react'
|
||||
import { WebApplication } from '@/Application/WebApplication'
|
||||
import { ViewControllerManager } from '@/Controllers/ViewControllerManager'
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import { Title, Subtitle } from '@/Components/Preferences/PreferencesComponents/Content'
|
||||
import Button from '@/Components/Button/Button'
|
||||
@@ -24,10 +23,9 @@ import { downloadOrShareBlobBasedOnPlatform } from '@/Utils/DownloadOrShareBased
|
||||
|
||||
type Props = {
|
||||
application: WebApplication
|
||||
viewControllerManager: ViewControllerManager
|
||||
}
|
||||
|
||||
const DataBackups = ({ application, viewControllerManager }: Props) => {
|
||||
const DataBackups = ({ application }: Props) => {
|
||||
const fileInputRef = useRef<HTMLInputElement>(null)
|
||||
const [isImportDataLoading, setIsImportDataLoading] = useState(false)
|
||||
const {
|
||||
@@ -36,7 +34,7 @@ const DataBackups = ({ application, viewControllerManager }: Props) => {
|
||||
setIsBackupEncrypted,
|
||||
setIsEncryptionEnabled,
|
||||
setEncryptionStatusString,
|
||||
} = viewControllerManager.accountMenuController
|
||||
} = application.accountMenuController
|
||||
|
||||
const refreshEncryptionStatus = useCallback(() => {
|
||||
const hasUser = application.hasAccount()
|
||||
@@ -73,16 +71,30 @@ const DataBackups = ({ application, viewControllerManager }: Props) => {
|
||||
})
|
||||
|
||||
if (isBackupEncrypted) {
|
||||
const filename = `Standard Notes Encrypted Backup and Import File - ${application
|
||||
.getArchiveService()
|
||||
.formattedDateForExports()}`
|
||||
const filename = `Standard Notes Encrypted Backup and Import File - ${application.archiveService.formattedDateForExports()}`
|
||||
const sanitizedFilename = sanitizeFileName(filename) + '.txt'
|
||||
void downloadOrShareBlobBasedOnPlatform(application, blobData, sanitizedFilename)
|
||||
void downloadOrShareBlobBasedOnPlatform({
|
||||
archiveService: application.archiveService,
|
||||
platform: application.platform,
|
||||
mobileDevice: application.mobileDevice,
|
||||
blob: blobData,
|
||||
filename: sanitizedFilename,
|
||||
isNativeMobileWeb: application.isNativeMobileWeb(),
|
||||
showToastOnAndroid: undefined,
|
||||
})
|
||||
} else {
|
||||
const zippedDecryptedItemsBlob = await application.getArchiveService().getZippedDecryptedItemsBlob(data)
|
||||
const filename = `Standard Notes Backup - ${application.getArchiveService().formattedDateForExports()}`
|
||||
const zippedDecryptedItemsBlob = await application.archiveService.getZippedDecryptedItemsBlob(data)
|
||||
const filename = `Standard Notes Backup - ${application.archiveService.formattedDateForExports()}`
|
||||
const sanitizedFilename = sanitizeFileName(filename) + '.zip'
|
||||
void downloadOrShareBlobBasedOnPlatform(application, zippedDecryptedItemsBlob, sanitizedFilename)
|
||||
void downloadOrShareBlobBasedOnPlatform({
|
||||
archiveService: application.archiveService,
|
||||
platform: application.platform,
|
||||
mobileDevice: application.mobileDevice,
|
||||
blob: zippedDecryptedItemsBlob,
|
||||
filename: sanitizedFilename,
|
||||
isNativeMobileWeb: application.isNativeMobileWeb(),
|
||||
showToastOnAndroid: undefined,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ const EmailBackups = ({ application }: Props) => {
|
||||
const hasAccount = application.hasAccount()
|
||||
|
||||
const loadEmailFrequencySetting = useCallback(async () => {
|
||||
if (!application.getUser()) {
|
||||
if (!application.sessions.getUser()) {
|
||||
return
|
||||
}
|
||||
setIsLoading(true)
|
||||
|
||||
@@ -41,7 +41,7 @@ const TextBackupsDesktop = ({ backupsService }: Props) => {
|
||||
}, [backupsEnabled, backupsService])
|
||||
|
||||
const performBackup = useCallback(async () => {
|
||||
void application.getDesktopService()?.saveDesktopBackup()
|
||||
void application.desktopManager?.saveDesktopBackup()
|
||||
}, [application])
|
||||
|
||||
return (
|
||||
|
||||
@@ -2,7 +2,6 @@ import { FunctionComponent } from 'react'
|
||||
import OfflineSubscription from '@/Components/Preferences/Panes/General/Advanced/OfflineSubscription'
|
||||
import { WebApplication } from '@/Application/WebApplication'
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import { ViewControllerManager } from '@/Controllers/ViewControllerManager'
|
||||
import PackagesPreferencesSection from '@/Components/Preferences/Panes/General/Advanced/Packages/Section'
|
||||
import { PackageProvider } from '@/Components/Preferences/Panes/General/Advanced/Packages/Provider/PackageProvider'
|
||||
import AccordionItem from '@/Components/Shared/AccordionItem'
|
||||
@@ -11,18 +10,17 @@ import PreferencesSegment from '../../../PreferencesComponents/PreferencesSegmen
|
||||
|
||||
type Props = {
|
||||
application: WebApplication
|
||||
viewControllerManager: ViewControllerManager
|
||||
extensionsLatestVersions: PackageProvider
|
||||
}
|
||||
|
||||
const Advanced: FunctionComponent<Props> = ({ application, viewControllerManager, extensionsLatestVersions }) => {
|
||||
const Advanced: FunctionComponent<Props> = ({ application, extensionsLatestVersions }) => {
|
||||
return (
|
||||
<PreferencesGroup>
|
||||
<PreferencesSegment>
|
||||
<AccordionItem title={'Advanced options'}>
|
||||
<div className="flex flex-row items-center">
|
||||
<div className="flex max-w-full flex-grow flex-col">
|
||||
<OfflineSubscription application={application} viewControllerManager={viewControllerManager} />
|
||||
<OfflineSubscription application={application} />
|
||||
<PackagesPreferencesSection
|
||||
className={'mt-3'}
|
||||
application={application}
|
||||
|
||||
@@ -3,7 +3,6 @@ import { Subtitle } from '@/Components/Preferences/PreferencesComponents/Content
|
||||
import DecoratedInput from '@/Components/Input/DecoratedInput'
|
||||
import Button from '@/Components/Button/Button'
|
||||
import { WebApplication } from '@/Application/WebApplication'
|
||||
import { ViewControllerManager } from '@/Controllers/ViewControllerManager'
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import { STRING_REMOVE_OFFLINE_KEY_CONFIRMATION } from '@/Constants/Strings'
|
||||
import { ButtonType, ClientDisplayableError } from '@standardnotes/snjs'
|
||||
@@ -11,7 +10,6 @@ import HorizontalSeparator from '@/Components/Shared/HorizontalSeparator'
|
||||
|
||||
type Props = {
|
||||
application: WebApplication
|
||||
viewControllerManager: ViewControllerManager
|
||||
onSuccess?: () => void
|
||||
}
|
||||
|
||||
@@ -46,7 +44,7 @@ const OfflineSubscription: FunctionComponent<Props> = ({ application, onSuccess
|
||||
return
|
||||
}
|
||||
|
||||
const signedInUser = application.getUser()
|
||||
const signedInUser = application.sessions.getUser()
|
||||
if (!signedInUser) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -34,12 +34,12 @@ const PackageEntry: FunctionComponent<PackageEntryProps> = ({ application, exten
|
||||
const toggleOfflineOnly = () => {
|
||||
const newOfflineOnly = !offlineOnly
|
||||
setOfflineOnly(newOfflineOnly)
|
||||
application
|
||||
.changeAndSaveItem<ComponentMutator>(extension, (mutator) => {
|
||||
application.changeAndSaveItem
|
||||
.execute<ComponentMutator>(extension, (mutator) => {
|
||||
mutator.offlineOnly = newOfflineOnly
|
||||
})
|
||||
.then((item) => {
|
||||
const component = item as ComponentInterface
|
||||
.then((result) => {
|
||||
const component = result.getValue() as ComponentInterface
|
||||
setOfflineOnly(component.offlineOnly)
|
||||
})
|
||||
.catch((e) => {
|
||||
@@ -49,12 +49,12 @@ const PackageEntry: FunctionComponent<PackageEntryProps> = ({ application, exten
|
||||
|
||||
const changeExtensionName = (newName: string) => {
|
||||
setExtensionName(newName)
|
||||
application
|
||||
.changeAndSaveItem<ComponentMutator>(extension, (mutator) => {
|
||||
application.changeAndSaveItem
|
||||
.execute<ComponentMutator>(extension, (mutator) => {
|
||||
mutator.name = newName
|
||||
})
|
||||
.then((item) => {
|
||||
const component = item as ComponentInterface
|
||||
.then((result) => {
|
||||
const component = result.getValue() as ComponentInterface
|
||||
setExtensionName(component.name)
|
||||
})
|
||||
.catch(console.error)
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { WebApplication } from '@/Application/WebApplication'
|
||||
import { ViewControllerManager } from '@/Controllers/ViewControllerManager'
|
||||
import { FunctionComponent } from 'react'
|
||||
import { PackageProvider } from '@/Components/Preferences/Panes/General/Advanced/Packages/Provider/PackageProvider'
|
||||
import { observer } from 'mobx-react-lite'
|
||||
@@ -13,24 +12,19 @@ import SmartViews from './SmartViews/SmartViews'
|
||||
import Moments from './Moments'
|
||||
|
||||
type Props = {
|
||||
viewControllerManager: ViewControllerManager
|
||||
application: WebApplication
|
||||
extensionsLatestVersions: PackageProvider
|
||||
}
|
||||
|
||||
const General: FunctionComponent<Props> = ({ viewControllerManager, application, extensionsLatestVersions }) => (
|
||||
const General: FunctionComponent<Props> = ({ application, extensionsLatestVersions }) => (
|
||||
<PreferencesPane>
|
||||
<Persistence application={application} />
|
||||
<Defaults application={application} />
|
||||
<Tools application={application} />
|
||||
<SmartViews application={application} featuresController={viewControllerManager.featuresController} />
|
||||
<SmartViews application={application} featuresController={application.featuresController} />
|
||||
<Moments application={application} />
|
||||
<LabsPane application={application} />
|
||||
<Advanced
|
||||
application={application}
|
||||
viewControllerManager={viewControllerManager}
|
||||
extensionsLatestVersions={extensionsLatestVersions}
|
||||
/>
|
||||
<Advanced application={application} extensionsLatestVersions={extensionsLatestVersions} />
|
||||
</PreferencesPane>
|
||||
)
|
||||
|
||||
|
||||
@@ -21,9 +21,9 @@ const Persistence = ({ application }: Props) => {
|
||||
setShouldPersistNoteState(shouldPersist)
|
||||
|
||||
if (shouldPersist) {
|
||||
application.controllers.persistValues()
|
||||
application.persistence.persistCurrentState()
|
||||
} else {
|
||||
application.controllers.clearPersistedValues()
|
||||
application.persistence.clearPersistedValues()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -88,7 +88,7 @@ export class EditSmartViewModalController {
|
||||
|
||||
this.setIsSaving(true)
|
||||
|
||||
await this.application.changeAndSaveItem<SmartViewMutator>(this.view, (mutator) => {
|
||||
await this.application.changeAndSaveItem.execute<SmartViewMutator>(this.view, (mutator) => {
|
||||
mutator.title = this.title
|
||||
mutator.iconString = (this.icon as string) || SmartViewDefaultIconName
|
||||
mutator.predicate = JSON.parse(this.predicateJson) as PredicateJsonForm
|
||||
|
||||
@@ -33,7 +33,7 @@ const SmartViews = ({ application, featuresController }: Props) => {
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
const disposeItemStream = application.streamItems([ContentType.TYPES.SmartView], () => {
|
||||
const disposeItemStream = application.items.streamItems([ContentType.TYPES.SmartView], () => {
|
||||
setSmartViews(application.items.getSmartViews().filter((view) => !isSystemView(view)))
|
||||
})
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ import { MouseEventHandler } from 'react'
|
||||
const HelpAndFeedback = ({ application }: { application: WebApplication }) => {
|
||||
const openLinkOnMobile = (link: string) => {
|
||||
if (application.isNativeMobileWeb()) {
|
||||
application.mobileDevice().openUrl(link)
|
||||
application.mobileDevice.openUrl(link)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -25,7 +25,6 @@ const HomeServerSettings = () => {
|
||||
const homeServerService = application.homeServer as HomeServerServiceInterface
|
||||
const featuresService = application.features
|
||||
const sessionsService = application.sessions
|
||||
const viewControllerManager = application.controllers
|
||||
|
||||
const logsTextarea = useRef<HTMLTextAreaElement>(null)
|
||||
|
||||
@@ -392,7 +391,6 @@ const HomeServerSettings = () => {
|
||||
{showOfflineSubscriptionActivation && (
|
||||
<OfflineSubscription
|
||||
application={application}
|
||||
viewControllerManager={viewControllerManager}
|
||||
onSuccess={() => {
|
||||
setIsAPremiumUser(true)
|
||||
setShowOfflineSubscriptionActivation(false)
|
||||
|
||||
@@ -39,7 +39,7 @@ const StatusIndicator = ({ status, className, homeServerService }: Props) => {
|
||||
|
||||
useEffect(() => {
|
||||
async function updateSignedInStatus() {
|
||||
const signedInUser = application.getUser()
|
||||
const signedInUser = application.sessions.getUser()
|
||||
if (signedInUser) {
|
||||
const isUsingHomeServer = await application.isUsingHomeServer()
|
||||
if (isUsingHomeServer) {
|
||||
|
||||
@@ -84,13 +84,14 @@ const Listed = ({ application }: Props) => {
|
||||
<Subtitle>What is Listed?</Subtitle>
|
||||
<Text>
|
||||
Listed is a free blogging platform that allows you to create a public journal published directly from your
|
||||
notes. {!application.getUser() && 'To get started, sign in or register for a Standard Notes account.'}
|
||||
notes.{' '}
|
||||
{!application.sessions.getUser() && 'To get started, sign in or register for a Standard Notes account.'}
|
||||
</Text>
|
||||
<a className="mt-2 text-info" target="_blank" href="https://listed.to" rel="noreferrer noopener">
|
||||
Learn more
|
||||
</a>
|
||||
</PreferencesSegment>
|
||||
{application.getUser() && (
|
||||
{application.sessions.getUser() && (
|
||||
<>
|
||||
<HorizontalSeparator classes="my-4" />
|
||||
<PreferencesSegment>
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
import { STRING_E2E_ENABLED, STRING_ENC_NOT_ENABLED, STRING_LOCAL_ENC_ENABLED } from '@/Constants/Strings'
|
||||
import { ViewControllerManager } from '@/Controllers/ViewControllerManager'
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import { FunctionComponent } from 'react'
|
||||
import { Title, Text } from '../../PreferencesComponents/Content'
|
||||
import PreferencesGroup from '../../PreferencesComponents/PreferencesGroup'
|
||||
import PreferencesSegment from '../../PreferencesComponents/PreferencesSegment'
|
||||
import EncryptionEnabled from './EncryptionEnabled'
|
||||
import { useApplication } from '@/Components/ApplicationProvider'
|
||||
|
||||
type Props = { viewControllerManager: ViewControllerManager }
|
||||
const Encryption: FunctionComponent = () => {
|
||||
const app = useApplication()
|
||||
|
||||
const Encryption: FunctionComponent<Props> = ({ viewControllerManager }) => {
|
||||
const app = viewControllerManager.application
|
||||
const hasUser = app.hasAccount()
|
||||
const hasPasscode = app.hasPasscode()
|
||||
const isEncryptionEnabled = app.isEncryptionAvailable()
|
||||
|
||||
@@ -14,7 +14,7 @@ const ErroredItems: FunctionComponent = () => {
|
||||
const [erroredItems, setErroredItems] = useState(application.items.invalidNonVaultedItems)
|
||||
|
||||
useEffect(() => {
|
||||
return application.streamItems(ContentType.TYPES.Any, () => {
|
||||
return application.items.streamItems(ContentType.TYPES.Any, () => {
|
||||
setErroredItems(application.items.invalidNonVaultedItems)
|
||||
})
|
||||
}, [application])
|
||||
@@ -83,7 +83,7 @@ const ErroredItems: FunctionComponent = () => {
|
||||
className="mr-2 mt-3 min-w-20"
|
||||
label="Export all"
|
||||
onClick={() => {
|
||||
void application.getArchiveService().downloadEncryptedItems(erroredItems)
|
||||
void application.archiveService.downloadEncryptedItems(erroredItems)
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
@@ -117,7 +117,7 @@ const ErroredItems: FunctionComponent = () => {
|
||||
className="mr-2 mt-3 min-w-20"
|
||||
label="Export"
|
||||
onClick={() => {
|
||||
void application.getArchiveService().downloadEncryptedItem(item)
|
||||
void application.archiveService.downloadEncryptedItem(item)
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import { WebApplication } from '@/Application/WebApplication'
|
||||
import { isIOS } from '@/Utils'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { MobileDeviceInterface } from '@standardnotes/services'
|
||||
import PreferencesSegment from '@/Components/Preferences/PreferencesComponents/PreferencesSegment'
|
||||
import { Title } from '@/Components/Preferences/PreferencesComponents/Content'
|
||||
import Button from '@/Components/Button/Button'
|
||||
import PreferencesGroup from '@/Components/Preferences/PreferencesComponents/PreferencesGroup'
|
||||
import { isIOS } from '@standardnotes/ui-services'
|
||||
|
||||
type Props = {
|
||||
application: WebApplication
|
||||
|
||||
@@ -14,7 +14,6 @@ import { alertDialog } from '@standardnotes/ui-services'
|
||||
import { FormEvent, useCallback, useEffect, useRef, useState } from 'react'
|
||||
import { ApplicationEvent, MobileUnlockTiming } from '@standardnotes/snjs'
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import { ViewControllerManager } from '@/Controllers/ViewControllerManager'
|
||||
import { Title, Text } from '@/Components/Preferences/PreferencesComponents/Content'
|
||||
import Button from '@/Components/Button/Button'
|
||||
import PreferencesGroup from '../../PreferencesComponents/PreferencesGroup'
|
||||
@@ -24,15 +23,13 @@ import { classNames } from '@standardnotes/utils'
|
||||
|
||||
type Props = {
|
||||
application: WebApplication
|
||||
viewControllerManager: ViewControllerManager
|
||||
}
|
||||
|
||||
const PasscodeLock = ({ application, viewControllerManager }: Props) => {
|
||||
const PasscodeLock = ({ application }: Props) => {
|
||||
const isNativeMobileWeb = application.isNativeMobileWeb()
|
||||
const keyStorageInfo = StringUtils.keyStorageInfo(application)
|
||||
|
||||
const { setIsEncryptionEnabled, setIsBackupEncrypted, setEncryptionStatusString } =
|
||||
viewControllerManager.accountMenuController
|
||||
const { setIsEncryptionEnabled, setIsBackupEncrypted, setEncryptionStatusString } = application.accountMenuController
|
||||
|
||||
const passcodeInputRef = useRef<HTMLInputElement>(null)
|
||||
|
||||
@@ -58,7 +55,7 @@ const PasscodeLock = ({ application, viewControllerManager }: Props) => {
|
||||
}
|
||||
|
||||
const reloadDesktopAutoLockInterval = useCallback(async () => {
|
||||
const interval = await application.getAutolockService()?.getAutoLockInterval()
|
||||
const interval = await application.autolockService?.getAutoLockInterval()
|
||||
setSelectedAutoLockInterval(interval)
|
||||
}, [application])
|
||||
|
||||
@@ -86,7 +83,7 @@ const PasscodeLock = ({ application, viewControllerManager }: Props) => {
|
||||
return
|
||||
}
|
||||
|
||||
await application.getAutolockService()?.setAutoLockInterval(interval)
|
||||
await application.autolockService?.setAutoLockInterval(interval)
|
||||
reloadDesktopAutoLockInterval().catch(console.error)
|
||||
}
|
||||
|
||||
@@ -99,7 +96,7 @@ const PasscodeLock = ({ application, viewControllerManager }: Props) => {
|
||||
await preventRefreshing(STRING_CONFIRM_APP_QUIT_DURING_PASSCODE_REMOVAL, async () => {
|
||||
if (await application.removePasscode()) {
|
||||
if (!isNativeMobileWeb) {
|
||||
await application.getAutolockService()?.deleteAutolockPreference()
|
||||
await application.autolockService?.deleteAutolockPreference()
|
||||
await reloadDesktopAutoLockInterval()
|
||||
}
|
||||
refreshEncryptionStatus()
|
||||
@@ -184,7 +181,7 @@ const PasscodeLock = ({ application, viewControllerManager }: Props) => {
|
||||
setPasscodeConfirmation(undefined)
|
||||
}
|
||||
|
||||
const autolockService = application.getAutolockService()
|
||||
const autolockService = application.autolockService
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
@@ -30,7 +30,7 @@ const Privacy: FunctionComponent<Props> = ({ application }: Props) => {
|
||||
}
|
||||
|
||||
const loadSettings = useCallback(async () => {
|
||||
if (!application.getUser()) {
|
||||
if (!application.sessions.getUser()) {
|
||||
return
|
||||
}
|
||||
setIsLoading(true)
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { NativeFeatureIdentifier, FeatureStatus } from '@standardnotes/snjs'
|
||||
|
||||
import { WebApplication } from '@/Application/WebApplication'
|
||||
import { ViewControllerManager } from '@/Controllers/ViewControllerManager'
|
||||
import { FunctionComponent } from 'react'
|
||||
import TwoFactorAuthWrapper from './TwoFactorAuth/TwoFactorAuthWrapper'
|
||||
import { MfaProps } from './TwoFactorAuth/MfaProps'
|
||||
@@ -16,7 +15,6 @@ import MultitaskingPrivacy from '@/Components/Preferences/Panes/Security/Multita
|
||||
import U2FWrapper from './U2F/U2FWrapper'
|
||||
|
||||
interface SecurityProps extends MfaProps {
|
||||
viewControllerManager: ViewControllerManager
|
||||
application: WebApplication
|
||||
}
|
||||
|
||||
@@ -26,23 +24,19 @@ const Security: FunctionComponent<SecurityProps> = (props) => {
|
||||
const isU2FFeatureAvailable =
|
||||
props.application.features.getFeatureStatus(
|
||||
NativeFeatureIdentifier.create(NativeFeatureIdentifier.TYPES.UniversalSecondFactor).getValue(),
|
||||
) === FeatureStatus.Entitled && props.userProvider.getUser() !== undefined
|
||||
) === FeatureStatus.Entitled && props.application.sessions.getUser() !== undefined
|
||||
|
||||
return (
|
||||
<PreferencesPane>
|
||||
<Encryption viewControllerManager={props.viewControllerManager} />
|
||||
<Encryption />
|
||||
{props.application.items.invalidNonVaultedItems.length > 0 && <ErroredItems />}
|
||||
<Protections application={props.application} />
|
||||
<TwoFactorAuthWrapper
|
||||
mfaProvider={props.mfaProvider}
|
||||
userProvider={props.userProvider}
|
||||
application={props.application}
|
||||
/>
|
||||
{isU2FFeatureAvailable && <U2FWrapper userProvider={props.userProvider} application={props.application} />}
|
||||
<TwoFactorAuthWrapper application={props.application} />
|
||||
{isU2FFeatureAvailable && <U2FWrapper application={props.application} />}
|
||||
{isNativeMobileWeb && <MultitaskingPrivacy application={props.application} />}
|
||||
<PasscodeLock viewControllerManager={props.viewControllerManager} application={props.application} />
|
||||
<PasscodeLock application={props.application} />
|
||||
{isNativeMobileWeb && <BiometricsLock application={props.application} />}
|
||||
{props.application.getUser() && <Privacy application={props.application} />}
|
||||
{props.application.sessions.getUser() && <Privacy application={props.application} />}
|
||||
</PreferencesPane>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
import { WebApplication } from '@/Application/WebApplication'
|
||||
import { MfaProvider, UserProvider } from '@/Components/Preferences/Providers'
|
||||
|
||||
export interface MfaProps {
|
||||
userProvider: UserProvider
|
||||
mfaProvider: MfaProvider
|
||||
application: WebApplication
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user