Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export interface ApplicationInterface {
createDecryptedBackupFile(): Promise<BackupFile | undefined>
hasPasscode(): boolean
lock(): Promise<void>
softLockBiometrics(): void
setValue(key: string, value: unknown, mode?: StorageValueModes): void
getValue(key: string, mode?: StorageValueModes): unknown
removeValue(key: string, mode?: StorageValueModes): Promise<void>
Expand Down
2 changes: 2 additions & 0 deletions packages/services/src/Domain/Event/ApplicationEvent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,4 +62,6 @@ export enum ApplicationEvent {
UnprotectedSessionExpired = 29,
/** Called when the app first launches and after first sync request made after sign in */
CompletedInitialSync = 30,
BiometricsSoftLockEngaged = 31,
BiometricsSoftLockDisengaged = 32,
}
19 changes: 19 additions & 0 deletions packages/snjs/lib/Application/Application.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ import {
SubscriptionManager,
WorkspaceClientInterface,
WorkspaceManager,
ChallengePrompt,
} from '@standardnotes/services'
import { FilesClientInterface } from '@standardnotes/files'
import { ComputePrivateUsername } from '@standardnotes/encryption'
Expand Down Expand Up @@ -929,6 +930,24 @@ export class SNApplication
return this.deinit(this.getDeinitMode(), DeinitSource.Lock)
}

public softLockBiometrics(): void {
const challenge = new Challenge(
[new ChallengePrompt(ChallengeValidation.Biometric)],
ChallengeReason.ApplicationUnlock,
false,
)

void this.promptForCustomChallenge(challenge)

void this.notifyEvent(ApplicationEvent.BiometricsSoftLockEngaged)

this.addChallengeObserver(challenge, {
onComplete: () => {
void this.notifyEvent(ApplicationEvent.BiometricsSoftLockDisengaged)
},
})
}

isNativeMobileWeb() {
return this.environment === Environment.NativeMobileWeb
}
Expand Down
6 changes: 4 additions & 2 deletions packages/web/src/javascripts/Application/Application.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ import {
ThemeManager,
WebAlertService,
} from '@standardnotes/ui-services'
import { MobileWebReceiver } from './MobileWebReceiver'
import { MobileWebReceiver } from '../NativeMobileWeb/MobileWebReceiver'
import { AndroidBackHandler } from '@/NativeMobileWeb/AndroidBackHandler'
import { PrefDefaults } from '@/Constants/PrefDefaults'
import { setViewportHeightWithFallback } from '@/setViewportHeightWithFallback'
Expand Down Expand Up @@ -289,8 +289,10 @@ export class WebApplication extends SNApplication implements WebApplicationInter
const passcodeLockImmediately = hasPasscode && passcodeTiming === MobileUnlockTiming.Immediately
const biometricsLockImmediately = hasBiometrics && biometricsTiming === MobileUnlockTiming.Immediately

if (passcodeLockImmediately || biometricsLockImmediately) {
if (passcodeLockImmediately) {
await this.lock()
} else if (biometricsLockImmediately) {
this.softLockBiometrics()
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,10 @@ const ApplicationView: FunctionComponent<Props> = ({ application, mainApplicatio
alertDialog({
text: 'Unable to write to local database. Please restart the app and try again.',
}).catch(console.error)
} else if (eventName === ApplicationEvent.BiometricsSoftLockEngaged) {
setNeedsUnlock(true)
} else if (eventName === ApplicationEvent.BiometricsSoftLockDisengaged) {
setNeedsUnlock(false)
}
})

Expand Down Expand Up @@ -197,7 +201,7 @@ const ApplicationView: FunctionComponent<Props> = ({ application, mainApplicatio
return (
<AndroidBackHandlerProvider application={application}>
<DarkModeHandler application={application} />
<ResponsivePaneProvider>
<ResponsivePaneProvider paneController={application.getViewControllerManager().paneController}>
<PremiumModalProvider application={application} viewControllerManager={viewControllerManager}>
<div className={platformString + ' main-ui-view sn-component'}>
<div id="app" className="app app-column-container" ref={appColumnContainerRef}>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { ElementIds } from '@/Constants/ElementIDs'
import { useAndroidBackHandler } from '@/NativeMobileWeb/useAndroidBackHandler'
import { isMobileScreen } from '@/Utils'
import {
useEffect,
ReactNode,
Expand All @@ -15,6 +14,8 @@ import {
MutableRefObject,
} from 'react'
import { AppPaneId } from './AppPaneMetadata'
import { PaneController } from '../../Controllers/PaneController'
import { observer } from 'mobx-react-lite'

type ResponsivePaneData = {
selectedPane: AppPaneId
Expand All @@ -39,6 +40,10 @@ type ChildrenProps = {
children: ReactNode
}

type ProviderProps = {
paneController: PaneController
} & ChildrenProps

function useStateRef<State>(state: State): MutableRefObject<State> {
const ref = useRef<State>(state)

Expand All @@ -51,21 +56,17 @@ function useStateRef<State>(state: State): MutableRefObject<State> {

const MemoizedChildren = memo(({ children }: ChildrenProps) => <div>{children}</div>)

const ResponsivePaneProvider = ({ children }: ChildrenProps) => {
const [currentSelectedPane, setCurrentSelectedPane] = useState<AppPaneId>(
isMobileScreen() ? AppPaneId.Items : AppPaneId.Editor,
)
const ResponsivePaneProvider = ({ paneController, children }: ProviderProps) => {
const currentSelectedPane = paneController.currentPane
const previousSelectedPane = paneController.previousPane
const currentSelectedPaneRef = useStateRef<AppPaneId>(currentSelectedPane)
const [previousSelectedPane, setPreviousSelectedPane] = useState<AppPaneId>(
isMobileScreen() ? AppPaneId.Items : AppPaneId.Editor,
)

const toggleAppPane = useCallback(
(paneId: AppPaneId) => {
setPreviousSelectedPane(currentSelectedPane)
setCurrentSelectedPane(paneId)
paneController.setPreviousPane(currentSelectedPane)
paneController.setCurrentPane(paneId)
},
[currentSelectedPane],
[paneController, currentSelectedPane],
)

useEffect(() => {
Expand Down Expand Up @@ -122,4 +123,4 @@ const ResponsivePaneProvider = ({ children }: ChildrenProps) => {
)
}

export default ResponsivePaneProvider
export default observer(ResponsivePaneProvider)
26 changes: 26 additions & 0 deletions packages/web/src/javascripts/Controllers/PaneController.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { AppPaneId } from './../Components/ResponsivePane/AppPaneMetadata'
import { isMobileScreen } from '@/Utils'
import { makeObservable, observable, action } from 'mobx'

export class PaneController {
currentPane: AppPaneId = isMobileScreen() ? AppPaneId.Items : AppPaneId.Editor
previousPane: AppPaneId = isMobileScreen() ? AppPaneId.Items : AppPaneId.Editor

constructor() {
makeObservable(this, {
currentPane: observable,
previousPane: observable,

setCurrentPane: action,
setPreviousPane: action,
})
}

setCurrentPane(pane: AppPaneId): void {
this.currentPane = pane
}

setPreviousPane(pane: AppPaneId): void {
this.previousPane = pane
}
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { PaneController } from './PaneController'
import { RouteType, storage, StorageKey } from '@standardnotes/ui-services'
import { WebApplication } from '@/Application/Application'
import { AccountMenuController } from '@/Controllers/AccountMenu/AccountMenuController'
Expand Down Expand Up @@ -56,6 +57,7 @@ export class ViewControllerManager {
readonly selectionController: SelectedItemsController
readonly historyModalController: HistoryModalController
readonly linkingController: LinkingController
readonly paneController: PaneController

public isSessionsModalVisible = false

Expand All @@ -71,6 +73,8 @@ export class ViewControllerManager {

this.subscriptionManager = application.subscriptions

this.paneController = new PaneController()

this.preferencesController = new PreferencesController(application, this.eventBus)

this.selectionController = new SelectedItemsController(application, this.eventBus)
Expand Down Expand Up @@ -207,6 +211,7 @@ export class ViewControllerManager {

this.historyModalController.deinit()
;(this.historyModalController as unknown) = undefined
;(this.paneController as unknown) = undefined

destroyAllObjectProperties(this)
}
Expand Down