internal: change password preprocessing step (#2347)

This commit is contained in:
Mo
2023-07-06 08:51:06 -05:00
committed by GitHub
parent 5c6ccaf4e1
commit c8e52b667c
39 changed files with 647 additions and 332 deletions

View File

@@ -0,0 +1,26 @@
import { PreferencesMenuItem } from './PreferencesMenuItem'
export const PREFERENCES_MENU_ITEMS: PreferencesMenuItem[] = [
{ id: 'whats-new', label: "What's New", icon: 'asterisk', order: 0 },
{ id: 'account', label: 'Account', icon: 'user', order: 1 },
{ id: 'general', label: 'General', icon: 'settings', order: 3 },
{ id: 'security', label: 'Security', icon: 'security', order: 4 },
{ id: 'backups', label: 'Backups', icon: 'restore', order: 5 },
{ id: 'appearance', label: 'Appearance', icon: 'themes', order: 6 },
{ id: 'listed', label: 'Listed', icon: 'listed', order: 7 },
{ id: 'shortcuts', label: 'Shortcuts', icon: 'keyboard', order: 8 },
{ id: 'accessibility', label: 'Accessibility', icon: 'accessibility', order: 9 },
{ id: 'get-free-month', label: 'Get a free month', icon: 'star', order: 10 },
{ id: 'help-feedback', label: 'Help & feedback', icon: 'help', order: 11 },
]
export const READY_PREFERENCES_MENU_ITEMS: PreferencesMenuItem[] = [
{ id: 'whats-new', label: "What's New", icon: 'asterisk', order: 0 },
{ id: 'account', label: 'Account', icon: 'user', order: 1 },
{ id: 'general', label: 'General', icon: 'settings', order: 3 },
{ id: 'security', label: 'Security', icon: 'security', order: 4 },
{ id: 'backups', label: 'Backups', icon: 'restore', order: 5 },
{ id: 'appearance', label: 'Appearance', icon: 'themes', order: 6 },
{ id: 'listed', label: 'Listed', icon: 'listed', order: 7 },
{ id: 'help-feedback', label: 'Help & feedback', icon: 'help', order: 11 },
]

View File

@@ -0,0 +1,10 @@
import { IconType } from '@standardnotes/snjs'
import { PreferenceId } from '@standardnotes/ui-services'
export interface PreferencesMenuItem {
readonly id: PreferenceId
readonly icon: IconType
readonly label: string
readonly order: number
readonly hasBubble?: boolean
}

View File

@@ -0,0 +1,100 @@
import { action, makeAutoObservable, observable } from 'mobx'
import { WebApplication } from '@/Application/WebApplication'
import { PackageProvider } from '../Panes/General/Advanced/Packages/Provider/PackageProvider'
import { securityPrefsHasBubble } from '../Panes/Security/securityPrefsHasBubble'
import { PreferenceId } from '@standardnotes/ui-services'
import { isDesktopApplication } from '@/Utils'
import { featureTrunkHomeServerEnabled, featureTrunkVaultsEnabled } from '@/FeatureTrunk'
import { PreferencesMenuItem } from './PreferencesMenuItem'
import { SelectableMenuItem } from './SelectableMenuItem'
import { PREFERENCES_MENU_ITEMS, READY_PREFERENCES_MENU_ITEMS } from './MenuItems'
/**
* Unlike PreferencesController, the PreferencesSessionController is ephemeral and bound to a single opening of the
* Preferences menu. It is created and destroyed each time the menu is opened and closed.
*/
export class PreferencesSessionController {
private _selectedPane: PreferenceId = 'account'
private _menu: PreferencesMenuItem[]
private _extensionLatestVersions: PackageProvider = new PackageProvider(new Map())
constructor(private application: WebApplication, private readonly _enableUnfinishedFeatures: boolean) {
const menuItems = this._enableUnfinishedFeatures
? PREFERENCES_MENU_ITEMS.slice()
: READY_PREFERENCES_MENU_ITEMS.slice()
if (featureTrunkVaultsEnabled()) {
menuItems.push({ id: 'vaults', label: 'Vaults', icon: 'safe-square', order: 5 })
}
if (featureTrunkHomeServerEnabled() && isDesktopApplication()) {
menuItems.push({ id: 'home-server', label: 'Home Server', icon: 'server', order: 5 })
}
this._menu = menuItems.sort((a, b) => a.order - b.order)
this.loadLatestVersions()
makeAutoObservable<
PreferencesSessionController,
'_selectedPane' | '_twoFactorAuth' | '_extensionPanes' | '_extensionLatestVersions' | 'loadLatestVersions'
>(this, {
_twoFactorAuth: observable,
_selectedPane: observable,
_extensionPanes: observable.ref,
_extensionLatestVersions: observable.ref,
loadLatestVersions: action,
})
}
private loadLatestVersions(): void {
PackageProvider.load()
.then((versions) => {
if (versions) {
this._extensionLatestVersions = versions
}
})
.catch(console.error)
}
get extensionsLatestVersions(): PackageProvider {
return this._extensionLatestVersions
}
get menuItems(): SelectableMenuItem[] {
const menuItems = this._menu.map((preference) => {
const item: SelectableMenuItem = {
...preference,
selected: preference.id === this._selectedPane,
hasBubble: this.sectionHasBubble(preference.id),
}
return item
})
return menuItems
}
get selectedMenuItem(): PreferencesMenuItem | undefined {
return this._menu.find((item) => item.id === this._selectedPane)
}
get selectedPaneId(): PreferenceId {
if (this.selectedMenuItem != undefined) {
return this.selectedMenuItem.id
}
return 'account'
}
selectPane = (key: PreferenceId) => {
this._selectedPane = key
}
sectionHasBubble(id: PreferenceId): boolean {
if (id === 'security') {
return securityPrefsHasBubble(this.application)
}
return false
}
}

View File

@@ -0,0 +1,5 @@
import { PreferencesMenuItem } from './PreferencesMenuItem'
export interface SelectableMenuItem extends PreferencesMenuItem {
selected: boolean
}

View File

@@ -1,6 +1,6 @@
import { FunctionComponent } from 'react'
import { observer } from 'mobx-react-lite'
import { PreferencesMenu } from './PreferencesMenu'
import { PreferencesSessionController } from './Controller/PreferencesSessionController'
import Backups from '@/Components/Preferences/Panes/Backups/Backups'
import Appearance from './Panes/Appearance'
import General from './Panes/General/General'
@@ -13,7 +13,7 @@ import WhatsNew from './Panes/WhatsNew/WhatsNew'
import HomeServer from './Panes/HomeServer/HomeServer'
import Vaults from './Panes/Vaults/Vaults'
const PaneSelector: FunctionComponent<PreferencesProps & { menu: PreferencesMenu }> = ({
const PaneSelector: FunctionComponent<PreferencesProps & { menu: PreferencesSessionController }> = ({
menu,
viewControllerManager,
application,

View File

@@ -1,10 +1,10 @@
import { ViewControllerManager } from '@/Controllers/ViewControllerManager'
import { observer } from 'mobx-react-lite'
import { Fragment, FunctionComponent, useState } from 'react'
import { Fragment, FunctionComponent, useEffect, useState } from 'react'
import { Text, Title, Subtitle } from '@/Components/Preferences/PreferencesComponents/Content'
import {
ButtonType,
ClientDisplayableError,
ContentType,
DisplayStringForContentType,
EncryptedItemInterface,
} from '@standardnotes/snjs'
@@ -12,13 +12,18 @@ import Button from '@/Components/Button/Button'
import HorizontalSeparator from '@/Components/Shared/HorizontalSeparator'
import PreferencesSegment from '../../PreferencesComponents/PreferencesSegment'
import PreferencesGroup from '../../PreferencesComponents/PreferencesGroup'
import { ErrorCircle } from '@/Components/UIElements/ErrorCircle'
import { useApplication } from '@/Components/ApplicationProvider'
type Props = { viewControllerManager: ViewControllerManager }
const ErroredItems: FunctionComponent = () => {
const application = useApplication()
const [erroredItems, setErroredItems] = useState(application.items.invalidNonVaultedItems)
const ErroredItems: FunctionComponent<Props> = ({ viewControllerManager }: Props) => {
const app = viewControllerManager.application
const [erroredItems, setErroredItems] = useState(app.items.invalidNonVaultedItems)
useEffect(() => {
return application.streamItems(ContentType.Any, () => {
setErroredItems(application.items.invalidNonVaultedItems)
})
}, [application])
const getContentTypeDisplay = (item: EncryptedItemInterface): string => {
const display = DisplayStringForContentType(item.content_type)
@@ -34,7 +39,7 @@ const ErroredItems: FunctionComponent<Props> = ({ viewControllerManager }: Props
}
const deleteItems = async (items: EncryptedItemInterface[]): Promise<void> => {
const confirmed = await app.alertService.confirm(
const confirmed = await application.alertService.confirm(
`Are you sure you want to permanently delete ${items.length} item(s)?`,
undefined,
'Delete',
@@ -44,30 +49,35 @@ const ErroredItems: FunctionComponent<Props> = ({ viewControllerManager }: Props
return
}
void app.mutator.deleteItems(items).then(() => {
void app.sync.sync()
void application.mutator.deleteItems(items).then(() => {
void application.sync.sync()
})
setErroredItems(app.items.invalidItems)
setErroredItems(application.items.invalidItems)
}
const attemptDecryption = (item: EncryptedItemInterface): void => {
const errorOrTrue = app.canAttemptDecryptionOfItem(item)
const errorOrTrue = application.canAttemptDecryptionOfItem(item)
if (errorOrTrue instanceof ClientDisplayableError) {
void app.alertService.showErrorAlert(errorOrTrue)
void application.alertService.showErrorAlert(errorOrTrue)
return
}
app.presentKeyRecoveryWizard()
application.presentKeyRecoveryWizard()
}
if (erroredItems.length === 0) {
return null
}
return (
<PreferencesGroup>
<PreferencesSegment>
<Title>
Error decrypting items <span className="ml-1 text-warning"></span>
<Title className="flex flex-row items-center gap-2">
<ErrorCircle />
Error decrypting items
</Title>
<Text>{`${erroredItems.length} items are errored and could not be decrypted.`}</Text>
<div className="flex">
@@ -75,7 +85,7 @@ const ErroredItems: FunctionComponent<Props> = ({ viewControllerManager }: Props
className="mt-3 mr-2 min-w-20"
label="Export all"
onClick={() => {
void app.getArchiveService().downloadEncryptedItems(erroredItems)
void application.getArchiveService().downloadEncryptedItems(erroredItems)
}}
/>
<Button
@@ -95,10 +105,8 @@ const ErroredItems: FunctionComponent<Props> = ({ viewControllerManager }: Props
<div className="flex items-center justify-between">
<div className="flex flex-col">
<Subtitle>{`${getContentTypeDisplay(item)} created on ${item.createdAtString}`}</Subtitle>
<Text>
<div>Item ID: {item.uuid}</div>
<div>Last Modified: {item.updatedAtString}</div>
</Text>
<Text>Item ID: {item.uuid}</Text>
<Text>Last Modified: {item.updatedAtString}</Text>
<div className="flex">
<Button
className="mt-3 mr-2 min-w-20"
@@ -111,7 +119,7 @@ const ErroredItems: FunctionComponent<Props> = ({ viewControllerManager }: Props
className="mt-3 mr-2 min-w-20"
label="Export"
onClick={() => {
void app.getArchiveService().downloadEncryptedItem(item)
void application.getArchiveService().downloadEncryptedItem(item)
}}
/>
<Button

View File

@@ -30,9 +30,7 @@ const Security: FunctionComponent<SecurityProps> = (props) => {
return (
<PreferencesPane>
<Encryption viewControllerManager={props.viewControllerManager} />
{props.application.items.invalidNonVaultedItems.length > 0 && (
<ErroredItems viewControllerManager={props.viewControllerManager} />
)}
{props.application.items.invalidNonVaultedItems.length > 0 && <ErroredItems />}
<Protections application={props.application} />
<TwoFactorAuthWrapper
mfaProvider={props.mfaProvider}

View File

@@ -25,7 +25,7 @@ const ContactItem = ({ contact }: Props) => {
return (
<>
<ModalOverlay isOpen={isContactModalOpen} close={closeContactModal}>
<EditContactModal editContactUuid={contact.uuid} onCloseDialog={closeContactModal} />
<EditContactModal editContactUuid={contact.contactUuid} onCloseDialog={closeContactModal} />
</ModalOverlay>
<div className="bg-gray-100 flex flex-row gap-3.5 rounded-lg py-2.5 px-3.5 shadow-md">

View File

@@ -5,33 +5,36 @@ import ModalOverlay from '@/Components/Modal/ModalOverlay'
import { PendingSharedVaultInviteRecord } from '@standardnotes/snjs'
import { useCallback, useState } from 'react'
import EditContactModal from '../Contacts/EditContactModal'
import { CheckmarkCircle } from '../../../../UIElements/CheckmarkCircle'
type Props = {
invite: PendingSharedVaultInviteRecord
inviteRecord: PendingSharedVaultInviteRecord
}
const InviteItem = ({ invite }: Props) => {
const InviteItem = ({ inviteRecord }: Props) => {
const application = useApplication()
const [isAddContactModalOpen, setIsAddContactModalOpen] = useState(false)
const isTrusted = invite.trusted
const inviteData = invite.message.data
const isTrusted = inviteRecord.trusted
const inviteData = inviteRecord.message.data
const addAsTrustedContact = useCallback(() => {
setIsAddContactModalOpen(true)
}, [])
const acceptInvite = useCallback(async () => {
await application.sharedVaults.acceptPendingSharedVaultInvite(invite)
}, [application.sharedVaults, invite])
await application.sharedVaults.acceptPendingSharedVaultInvite(inviteRecord)
}, [application.sharedVaults, inviteRecord])
const closeAddContactModal = () => setIsAddContactModalOpen(false)
const collaborationId = application.contacts.getCollaborationIDFromInvite(invite.invite)
const collaborationId = application.contacts.getCollaborationIDFromInvite(inviteRecord.invite)
const trustedContact = application.contacts.findTrustedContactForInvite(inviteRecord.invite)
return (
<>
<ModalOverlay isOpen={isAddContactModalOpen} close={closeAddContactModal}>
<EditContactModal fromInvite={invite} onCloseDialog={closeAddContactModal} />
<EditContactModal fromInvite={inviteRecord} onCloseDialog={closeAddContactModal} />
</ModalOverlay>
<div className="bg-gray-100 flex flex-row gap-3.5 rounded-lg py-2.5 px-3.5 shadow-md">
@@ -41,9 +44,16 @@ const InviteItem = ({ invite }: Props) => {
<span className="mr-auto overflow-hidden text-ellipsis text-sm">
Vault Description: {inviteData.metadata.description}
</span>
<span className="mr-auto overflow-hidden text-ellipsis text-sm">
Sender CollaborationID: {collaborationId}
</span>
{trustedContact ? (
<div className="flex flex-row gap-2">
<span className="overflow-hidden text-ellipsis text-sm">Trusted Sender: {trustedContact.name}</span>
<CheckmarkCircle />
</div>
) : (
<span className="mr-auto overflow-hidden text-ellipsis text-sm">
Sender CollaborationID: {collaborationId}
</span>
)}
<div className="mt-2.5 flex flex-row">
{isTrusted ? (

View File

@@ -40,31 +40,36 @@ const Vaults = () => {
setVaults(vaultService.getVaults())
}, [vaultService])
const fetchInvites = useCallback(async () => {
await sharedVaultService.downloadInboundInvites()
const invites = sharedVaultService.getCachedPendingInviteRecords()
setInvites(invites)
const updateInvites = useCallback(async () => {
setInvites(sharedVaultService.getCachedPendingInviteRecords())
}, [sharedVaultService])
const updateContacts = useCallback(async () => {
setContacts(contactService.getAllContacts())
}, [contactService])
const updateAllData = useCallback(async () => {
await Promise.all([updateVaults(), updateInvites(), updateContacts()])
}, [updateContacts, updateInvites, updateVaults])
useEffect(() => {
return application.sharedVaults.addEventObserver((event) => {
if (event === SharedVaultServiceEvent.SharedVaultStatusChanged) {
void fetchInvites()
void updateAllData()
}
})
})
}, [application.sharedVaults, updateAllData])
useEffect(() => {
return application.streamItems([ContentType.VaultListing, ContentType.TrustedContact], () => {
void updateVaults()
void fetchInvites()
void updateContacts()
void updateAllData()
})
}, [application, updateVaults, fetchInvites, updateContacts])
}, [application, updateAllData])
useEffect(() => {
void sharedVaultService.downloadInboundInvites()
void updateAllData()
}, [updateAllData, sharedVaultService])
const createNewVault = useCallback(async () => {
setIsVaultModalOpen(true)
@@ -74,12 +79,6 @@ const Vaults = () => {
setIsAddContactModalOpen(true)
}, [])
useEffect(() => {
void updateVaults()
void fetchInvites()
void updateContacts()
}, [updateContacts, updateVaults, fetchInvites])
return (
<>
<ModalOverlay isOpen={isAddContactModalOpen} close={closeAddContactModal}>
@@ -95,7 +94,7 @@ const Vaults = () => {
<Title>Incoming Invites</Title>
<div className="my-2 flex flex-col">
{invites.map((invite) => {
return <InviteItem invite={invite} key={invite.invite.uuid} />
return <InviteItem inviteRecord={invite} key={invite.invite.uuid} />
})}
</div>
</PreferencesSegment>

View File

@@ -28,9 +28,8 @@ export const VaultModalInvites = ({
<div className="mb-3 text-lg">Pending Invites</div>
{invites.map((invite) => {
const contact = application.contacts.findTrustedContactForInvite(invite)
return (
<div className="bg-gray-100 flex flex-row gap-3.5 rounded-lg py-2.5 px-3.5 shadow-md">
<div key={invite.uuid} className="bg-gray-100 flex flex-row gap-3.5 rounded-lg py-2.5 px-3.5 shadow-md">
<Icon type={'user'} size="custom" className="mt-2.5 h-5.5 w-5.5 flex-shrink-0" />
<div className="flex flex-col gap-2 py-1.5">
<span className="mr-auto overflow-hidden text-ellipsis text-base font-bold">

View File

@@ -1,11 +1,11 @@
import { FunctionComponent } from 'react'
import { observer } from 'mobx-react-lite'
import { PreferencesMenu } from './PreferencesMenu'
import { PreferencesSessionController } from './Controller/PreferencesSessionController'
import PreferencesMenuView from './PreferencesMenuView'
import PaneSelector from './PaneSelector'
import { PreferencesProps } from './PreferencesProps'
const PreferencesCanvas: FunctionComponent<PreferencesProps & { menu: PreferencesMenu }> = (props) => (
const PreferencesCanvas: FunctionComponent<PreferencesProps & { menu: PreferencesSessionController }> = (props) => (
<div className="flex min-h-0 flex-grow flex-col md:flex-row md:justify-between">
<PreferencesMenuView menu={props.menu} />
<div className="min-h-0 flex-grow overflow-auto bg-contrast">

View File

@@ -1,6 +1,7 @@
import Icon from '@/Components/Icon/Icon'
import { FunctionComponent } from 'react'
import { IconType } from '@standardnotes/snjs'
import { ErrorCircle } from '@/Components/UIElements/ErrorCircle'
interface Props {
iconType: IconType
@@ -23,7 +24,11 @@ const PreferencesMenuItem: FunctionComponent<Props> = ({ iconType, label, select
<Icon className={`icon text-base ${selected ? 'text-info' : 'text-neutral'}`} type={iconType} />
<div className="min-w-1" />
{label}
{hasBubble && <span className="ml-1 text-warning"></span>}
{hasBubble && (
<span className="ml-2">
<ErrorCircle />
</span>
)}
</div>
)

View File

@@ -1,139 +0,0 @@
import { action, makeAutoObservable, observable } from 'mobx'
import { IconType } from '@standardnotes/snjs'
import { WebApplication } from '@/Application/WebApplication'
import { PackageProvider } from './Panes/General/Advanced/Packages/Provider/PackageProvider'
import { securityPrefsHasBubble } from './Panes/Security/securityPrefsHasBubble'
import { PreferenceId } from '@standardnotes/ui-services'
import { isDesktopApplication } from '@/Utils'
import { featureTrunkHomeServerEnabled, featureTrunkVaultsEnabled } from '@/FeatureTrunk'
interface PreferencesMenuItem {
readonly id: PreferenceId
readonly icon: IconType
readonly label: string
readonly order: number
readonly hasBubble?: boolean
}
interface SelectableMenuItem extends PreferencesMenuItem {
selected: boolean
}
/**
* Items are in order of appearance
*/
const PREFERENCES_MENU_ITEMS: PreferencesMenuItem[] = [
{ id: 'whats-new', label: "What's New", icon: 'asterisk', order: 0 },
{ id: 'account', label: 'Account', icon: 'user', order: 1 },
{ id: 'general', label: 'General', icon: 'settings', order: 3 },
{ id: 'security', label: 'Security', icon: 'security', order: 4 },
{ id: 'backups', label: 'Backups', icon: 'restore', order: 5 },
{ id: 'appearance', label: 'Appearance', icon: 'themes', order: 6 },
{ id: 'listed', label: 'Listed', icon: 'listed', order: 7 },
{ id: 'shortcuts', label: 'Shortcuts', icon: 'keyboard', order: 8 },
{ id: 'accessibility', label: 'Accessibility', icon: 'accessibility', order: 9 },
{ id: 'get-free-month', label: 'Get a free month', icon: 'star', order: 10 },
{ id: 'help-feedback', label: 'Help & feedback', icon: 'help', order: 11 },
]
const READY_PREFERENCES_MENU_ITEMS: PreferencesMenuItem[] = [
{ id: 'whats-new', label: "What's New", icon: 'asterisk', order: 0 },
{ id: 'account', label: 'Account', icon: 'user', order: 1 },
{ id: 'general', label: 'General', icon: 'settings', order: 3 },
{ id: 'security', label: 'Security', icon: 'security', order: 4 },
{ id: 'backups', label: 'Backups', icon: 'restore', order: 5 },
{ id: 'appearance', label: 'Appearance', icon: 'themes', order: 6 },
{ id: 'listed', label: 'Listed', icon: 'listed', order: 7 },
{ id: 'help-feedback', label: 'Help & feedback', icon: 'help', order: 11 },
]
const DESKTOP_PREFERENCES_MENU_ITEMS: PreferencesMenuItem[] = []
export class PreferencesMenu {
private _selectedPane: PreferenceId = 'account'
private _menu: PreferencesMenuItem[]
private _extensionLatestVersions: PackageProvider = new PackageProvider(new Map())
constructor(private application: WebApplication, private readonly _enableUnfinishedFeatures: boolean) {
if (featureTrunkVaultsEnabled()) {
PREFERENCES_MENU_ITEMS.splice(3, 0, { id: 'vaults', label: 'Vaults', icon: 'safe-square', order: 5 })
READY_PREFERENCES_MENU_ITEMS.splice(3, 0, { id: 'vaults', label: 'Vaults', icon: 'safe-square', order: 5 })
}
if (featureTrunkHomeServerEnabled()) {
DESKTOP_PREFERENCES_MENU_ITEMS.push({ id: 'home-server', label: 'Home Server', icon: 'server', order: 5 })
}
let menuItems = this._enableUnfinishedFeatures ? PREFERENCES_MENU_ITEMS : READY_PREFERENCES_MENU_ITEMS
if (isDesktopApplication()) {
menuItems = [...menuItems, ...DESKTOP_PREFERENCES_MENU_ITEMS]
}
this._menu = menuItems.sort((a, b) => a.order - b.order)
this.loadLatestVersions()
makeAutoObservable<
PreferencesMenu,
'_selectedPane' | '_twoFactorAuth' | '_extensionPanes' | '_extensionLatestVersions' | 'loadLatestVersions'
>(this, {
_twoFactorAuth: observable,
_selectedPane: observable,
_extensionPanes: observable.ref,
_extensionLatestVersions: observable.ref,
loadLatestVersions: action,
})
}
private loadLatestVersions(): void {
PackageProvider.load()
.then((versions) => {
if (versions) {
this._extensionLatestVersions = versions
}
})
.catch(console.error)
}
get extensionsLatestVersions(): PackageProvider {
return this._extensionLatestVersions
}
get menuItems(): SelectableMenuItem[] {
const menuItems = this._menu.map((preference) => {
const item: SelectableMenuItem = {
...preference,
selected: preference.id === this._selectedPane,
hasBubble: this.sectionHasBubble(preference.id),
}
return item
})
return menuItems
}
get selectedMenuItem(): PreferencesMenuItem | undefined {
return this._menu.find((item) => item.id === this._selectedPane)
}
get selectedPaneId(): PreferenceId {
if (this.selectedMenuItem != undefined) {
return this.selectedMenuItem.id
}
return 'account'
}
selectPane = (key: PreferenceId) => {
this._selectedPane = key
}
sectionHasBubble(id: PreferenceId): boolean {
if (id === 'security') {
return securityPrefsHasBubble(this.application)
}
return false
}
}

View File

@@ -3,11 +3,11 @@ import { FunctionComponent, useMemo } from 'react'
import Dropdown from '../Dropdown/Dropdown'
import { DropdownItem } from '../Dropdown/DropdownItem'
import PreferencesMenuItem from './PreferencesComponents/MenuItem'
import { PreferencesMenu } from './PreferencesMenu'
import { PreferencesSessionController } from './Controller/PreferencesSessionController'
import { PreferenceId } from '@standardnotes/ui-services'
type Props = {
menu: PreferencesMenu
menu: PreferencesSessionController
}
const PreferencesMenuView: FunctionComponent<Props> = ({ menu }) => {

View File

@@ -1,7 +1,7 @@
import RoundIconButton from '@/Components/Button/RoundIconButton'
import { FunctionComponent, useEffect, useMemo } from 'react'
import { observer } from 'mobx-react-lite'
import { PreferencesMenu } from './PreferencesMenu'
import { PreferencesSessionController } from './Controller/PreferencesSessionController'
import PreferencesCanvas from './PreferencesCanvas'
import { PreferencesProps } from './PreferencesProps'
import { useAndroidBackHandler } from '@/NativeMobileWeb/useAndroidBackHandler'
@@ -19,7 +19,7 @@ const PreferencesView: FunctionComponent<PreferencesProps> = ({
mfaProvider,
}) => {
const menu = useMemo(
() => new PreferencesMenu(application, viewControllerManager.enableUnfinishedFeatures),
() => new PreferencesSessionController(application, viewControllerManager.enableUnfinishedFeatures),
[viewControllerManager.enableUnfinishedFeatures, application],
)