fix(mobile): passcode timing options (#1744)
This commit is contained in:
@@ -483,7 +483,7 @@ export class ApplicationState extends ApplicationService {
|
||||
return
|
||||
}
|
||||
|
||||
const hasBiometrics = this.application.hasBiometrics()
|
||||
const hasBiometrics = this.application.protections.hasBiometricsEnabled()
|
||||
const hasPasscode = this.application.hasPasscode()
|
||||
const passcodeLockImmediately = hasPasscode && this.passcodeTiming === MobileUnlockTiming.Immediately
|
||||
const biometricsLockImmediately =
|
||||
@@ -571,19 +571,19 @@ export class ApplicationState extends ApplicationService {
|
||||
}
|
||||
|
||||
private async getScreenshotPrivacyEnabled(): Promise<boolean> {
|
||||
return this.application.getMobileScreenshotPrivacyEnabled()
|
||||
return this.application.protections.getMobileScreenshotPrivacyEnabled()
|
||||
}
|
||||
|
||||
private async getPasscodeTiming(): Promise<MobileUnlockTiming | undefined> {
|
||||
return this.application.getMobilePasscodeTiming()
|
||||
return this.application.protections.getMobilePasscodeTiming()
|
||||
}
|
||||
|
||||
private async getBiometricsTiming(): Promise<MobileUnlockTiming | undefined> {
|
||||
return this.application.getMobileBiometricsTiming()
|
||||
return this.application.protections.getMobileBiometricsTiming()
|
||||
}
|
||||
|
||||
public async setScreenshotPrivacyEnabled(enabled: boolean) {
|
||||
await this.application.setMobileScreenshotPrivacyEnabled(enabled)
|
||||
await this.application.protections.setMobileScreenshotPrivacyEnabled(enabled)
|
||||
this.screenshotPrivacyEnabled = enabled
|
||||
await (this.application.deviceInterface as MobileDevice).setAndroidScreenshotPrivacy(enabled)
|
||||
}
|
||||
|
||||
@@ -197,7 +197,7 @@ export const Authenticate = ({
|
||||
state: AuthenticationValueStateType.Pending,
|
||||
})
|
||||
|
||||
if (await application?.getMobileScreenshotPrivacyEnabled()) {
|
||||
if (application?.protections.getMobileScreenshotPrivacyEnabled()) {
|
||||
hide()
|
||||
}
|
||||
|
||||
|
||||
@@ -31,21 +31,23 @@ export const SecuritySection = (props: Props) => {
|
||||
const [hasBiometrics, setHasBiometrics] = useState(false)
|
||||
const [supportsBiometrics, setSupportsBiometrics] = useState(false)
|
||||
const [biometricsTimingOptions, setBiometricsTimingOptions] = useState(() =>
|
||||
application!.getBiometricsTimingOptions(),
|
||||
application!.protections.getMobileBiometricsTimingOptions(),
|
||||
)
|
||||
const [passcodeTimingOptions, setPasscodeTimingOptions] = useState(() =>
|
||||
application!.protections.getMobilePasscodeTimingOptions(),
|
||||
)
|
||||
const [passcodeTimingOptions, setPasscodeTimingOptions] = useState(() => application!.getPasscodeTimingOptions())
|
||||
|
||||
useEffect(() => {
|
||||
let mounted = true
|
||||
const getHasScreenshotPrivacy = async () => {
|
||||
const hasScreenshotPrivacyEnabled = application?.getMobileScreenshotPrivacyEnabled()
|
||||
const hasScreenshotPrivacyEnabled = application?.protections.getMobileScreenshotPrivacyEnabled()
|
||||
if (mounted) {
|
||||
setHasScreenshotPrivacy(hasScreenshotPrivacyEnabled)
|
||||
}
|
||||
}
|
||||
void getHasScreenshotPrivacy()
|
||||
const getHasBiometrics = async () => {
|
||||
const appHasBiometrics = application!.hasBiometrics()
|
||||
const appHasBiometrics = application!.protections.hasBiometricsEnabled()
|
||||
if (mounted) {
|
||||
setHasBiometrics(appHasBiometrics)
|
||||
}
|
||||
@@ -68,7 +70,7 @@ export const SecuritySection = (props: Props) => {
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
if (props.hasPasscode) {
|
||||
setPasscodeTimingOptions(() => application!.getPasscodeTimingOptions())
|
||||
setPasscodeTimingOptions(() => application!.protections.getMobilePasscodeTimingOptions())
|
||||
}
|
||||
}, [application, props.hasPasscode]),
|
||||
)
|
||||
@@ -126,12 +128,12 @@ export const SecuritySection = (props: Props) => {
|
||||
|
||||
const setBiometricsTiming = async (timing: MobileUnlockTiming) => {
|
||||
await application?.getAppState().setBiometricsTiming(timing)
|
||||
setBiometricsTimingOptions(() => application!.getBiometricsTimingOptions())
|
||||
setBiometricsTimingOptions(() => application!.protections.getMobileBiometricsTimingOptions())
|
||||
}
|
||||
|
||||
const setPasscodeTiming = async (timing: MobileUnlockTiming) => {
|
||||
await application?.getAppState().setPasscodeTiming(timing)
|
||||
setPasscodeTimingOptions(() => application!.getPasscodeTimingOptions())
|
||||
setPasscodeTimingOptions(() => application!.protections.getMobilePasscodeTimingOptions())
|
||||
}
|
||||
|
||||
const onScreenshotPrivacyPress = async () => {
|
||||
@@ -153,14 +155,14 @@ export const SecuritySection = (props: Props) => {
|
||||
void disableAuthentication('biometrics')
|
||||
} else {
|
||||
setHasBiometrics(true)
|
||||
await application?.enableBiometrics()
|
||||
await application?.protections.enableBiometrics()
|
||||
await setBiometricsTiming(MobileUnlockTiming.OnQuit)
|
||||
props.updateProtectionsAvailable()
|
||||
}
|
||||
}
|
||||
|
||||
const disableBiometrics = useCallback(async () => {
|
||||
if (await application?.disableBiometrics()) {
|
||||
if (await application?.protections.disableBiometrics()) {
|
||||
setHasBiometrics(false)
|
||||
props.updateProtectionsAvailable()
|
||||
}
|
||||
|
||||
@@ -45,7 +45,6 @@ import {
|
||||
FileService,
|
||||
SubscriptionClientInterface,
|
||||
SubscriptionManager,
|
||||
StorageValueModes,
|
||||
} from '@standardnotes/services'
|
||||
import { FilesClientInterface } from '@standardnotes/files'
|
||||
import { ComputePrivateWorkspaceIdentifier } from '@standardnotes/encryption'
|
||||
@@ -65,7 +64,6 @@ import { SNLog } from '../Log'
|
||||
import { Challenge, ChallengeResponse } from '../Services'
|
||||
import { ApplicationConstructorOptions, FullyResolvedApplicationOptions } from './Options/ApplicationOptions'
|
||||
import { ApplicationOptionsDefaults } from './Options/Defaults'
|
||||
import { MobileUnlockTiming } from '@Lib/Services/Protection/MobileUnlockTiming'
|
||||
|
||||
/** How often to automatically sync, in milliseconds */
|
||||
const DEFAULT_AUTO_SYNC_INTERVAL = 30_000
|
||||
@@ -899,24 +897,6 @@ export class SNApplication
|
||||
return this.launched
|
||||
}
|
||||
|
||||
public hasBiometrics(): boolean {
|
||||
return this.protectionService.hasBiometricsEnabled()
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns whether the operation was successful or not
|
||||
*/
|
||||
public enableBiometrics(): boolean {
|
||||
return this.protectionService.enableBiometrics()
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns whether the operation was successful or not
|
||||
*/
|
||||
public disableBiometrics(): Promise<boolean> {
|
||||
return this.protectionService.disableBiometrics()
|
||||
}
|
||||
|
||||
public hasPasscode(): boolean {
|
||||
return this.protocolService.hasPasscode()
|
||||
}
|
||||
@@ -936,38 +916,6 @@ export class SNApplication
|
||||
return this.deinit(this.getDeinitMode(), DeinitSource.Lock)
|
||||
}
|
||||
|
||||
setBiometricsTiming(timing: MobileUnlockTiming) {
|
||||
return this.protectionService.setBiometricsTiming(timing)
|
||||
}
|
||||
|
||||
getMobileScreenshotPrivacyEnabled(): boolean {
|
||||
return this.protectionService.getMobileScreenshotPrivacyEnabled()
|
||||
}
|
||||
|
||||
setMobileScreenshotPrivacyEnabled(isEnabled: boolean) {
|
||||
return this.protectionService.setMobileScreenshotPrivacyEnabled(isEnabled)
|
||||
}
|
||||
|
||||
getMobilePasscodeTiming(): MobileUnlockTiming | undefined {
|
||||
return this.getValue(StorageKey.MobilePasscodeTiming, StorageValueModes.Nonwrapped) as
|
||||
| MobileUnlockTiming
|
||||
| undefined
|
||||
}
|
||||
|
||||
getMobileBiometricsTiming(): MobileUnlockTiming | undefined {
|
||||
return this.getValue(StorageKey.MobileBiometricsTiming, StorageValueModes.Nonwrapped) as
|
||||
| MobileUnlockTiming
|
||||
| undefined
|
||||
}
|
||||
|
||||
getBiometricsTimingOptions() {
|
||||
return this.protectionService.getBiometricsTimingOptions()
|
||||
}
|
||||
|
||||
getPasscodeTimingOptions() {
|
||||
return this.protectionService.getPasscodeTimingOptions()
|
||||
}
|
||||
|
||||
isNativeMobileWeb() {
|
||||
return this.environment === Environment.NativeMobileWeb
|
||||
}
|
||||
|
||||
@@ -1,8 +1,19 @@
|
||||
import { ChallengeReason } from '@standardnotes/services'
|
||||
import { DecryptedItem } from '@standardnotes/models'
|
||||
import { TimingDisplayOption, MobileUnlockTiming } from './MobileUnlockTiming'
|
||||
|
||||
export interface ProtectionsClientInterface {
|
||||
authorizeProtectedActionForItems<T extends DecryptedItem>(files: T[], challengeReason: ChallengeReason): Promise<T[]>
|
||||
|
||||
authorizeItemAccess(item: DecryptedItem): Promise<boolean>
|
||||
getMobileBiometricsTiming(): MobileUnlockTiming | undefined
|
||||
getMobilePasscodeTiming(): MobileUnlockTiming | undefined
|
||||
setMobileBiometricsTiming(timing: MobileUnlockTiming): void
|
||||
setMobilePasscodeTiming(timing: MobileUnlockTiming): void
|
||||
setMobileScreenshotPrivacyEnabled(isEnabled: boolean): void
|
||||
getMobileScreenshotPrivacyEnabled(): boolean
|
||||
getMobilePasscodeTimingOptions(): TimingDisplayOption[]
|
||||
getMobileBiometricsTimingOptions(): TimingDisplayOption[]
|
||||
hasBiometricsEnabled(): boolean
|
||||
enableBiometrics(): boolean
|
||||
disableBiometrics(): Promise<boolean>
|
||||
}
|
||||
|
||||
@@ -2,3 +2,9 @@ export enum MobileUnlockTiming {
|
||||
Immediately = 'immediately',
|
||||
OnQuit = 'on-quit',
|
||||
}
|
||||
|
||||
export type TimingDisplayOption = {
|
||||
title: string
|
||||
key: MobileUnlockTiming
|
||||
selected: boolean
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ import {
|
||||
} from '@standardnotes/services'
|
||||
import { ProtectionsClientInterface } from './ClientInterface'
|
||||
import { ContentType } from '@standardnotes/common'
|
||||
import { MobileUnlockTiming } from './MobileUnlockTiming'
|
||||
import { MobileUnlockTiming, TimingDisplayOption } from './MobileUnlockTiming'
|
||||
|
||||
export enum ProtectionEvent {
|
||||
UnprotectedSessionBegan = 'UnprotectedSessionBegan',
|
||||
@@ -64,8 +64,8 @@ export const ProtectionSessionDurations = [
|
||||
*/
|
||||
export class SNProtectionService extends AbstractService<ProtectionEvent> implements ProtectionsClientInterface {
|
||||
private sessionExpiryTimeout = -1
|
||||
private mobilePasscodeTiming: MobileUnlockTiming | undefined = MobileUnlockTiming.Immediately
|
||||
private mobileBiometricsTiming: MobileUnlockTiming | undefined = MobileUnlockTiming.Immediately
|
||||
private mobilePasscodeTiming: MobileUnlockTiming | undefined = MobileUnlockTiming.OnQuit
|
||||
private mobileBiometricsTiming: MobileUnlockTiming | undefined = MobileUnlockTiming.OnQuit
|
||||
|
||||
constructor(
|
||||
private protocolService: EncryptionService,
|
||||
@@ -87,8 +87,8 @@ export class SNProtectionService extends AbstractService<ProtectionEvent> implem
|
||||
override handleApplicationStage(stage: ApplicationStage): Promise<void> {
|
||||
if (stage === ApplicationStage.LoadedDatabase_12) {
|
||||
this.updateSessionExpiryTimer(this.getSessionExpiryDate())
|
||||
this.mobilePasscodeTiming = this.getPasscodeTiming()
|
||||
this.mobileBiometricsTiming = this.getBiometricsTiming()
|
||||
this.mobilePasscodeTiming = this.getMobilePasscodeTiming()
|
||||
this.mobileBiometricsTiming = this.getMobileBiometricsTiming()
|
||||
}
|
||||
return Promise.resolve()
|
||||
}
|
||||
@@ -229,7 +229,7 @@ export class SNProtectionService extends AbstractService<ProtectionEvent> implem
|
||||
})
|
||||
}
|
||||
|
||||
getPasscodeTimingOptions() {
|
||||
getMobilePasscodeTimingOptions(): TimingDisplayOption[] {
|
||||
return [
|
||||
{
|
||||
title: 'Immediately',
|
||||
@@ -244,7 +244,7 @@ export class SNProtectionService extends AbstractService<ProtectionEvent> implem
|
||||
]
|
||||
}
|
||||
|
||||
getBiometricsTimingOptions() {
|
||||
getMobileBiometricsTimingOptions(): TimingDisplayOption[] {
|
||||
return [
|
||||
{
|
||||
title: 'Immediately',
|
||||
@@ -259,25 +259,32 @@ export class SNProtectionService extends AbstractService<ProtectionEvent> implem
|
||||
]
|
||||
}
|
||||
|
||||
private getBiometricsTiming(): MobileUnlockTiming | undefined {
|
||||
getMobileBiometricsTiming(): MobileUnlockTiming | undefined {
|
||||
return this.storageService.getValue<MobileUnlockTiming | undefined>(
|
||||
StorageKey.MobileBiometricsTiming,
|
||||
StorageValueModes.Nonwrapped,
|
||||
MobileUnlockTiming.OnQuit,
|
||||
)
|
||||
}
|
||||
|
||||
private getPasscodeTiming(): MobileUnlockTiming | undefined {
|
||||
getMobilePasscodeTiming(): MobileUnlockTiming | undefined {
|
||||
return this.storageService.getValue<MobileUnlockTiming | undefined>(
|
||||
StorageKey.MobilePasscodeTiming,
|
||||
StorageValueModes.Nonwrapped,
|
||||
MobileUnlockTiming.OnQuit,
|
||||
)
|
||||
}
|
||||
|
||||
async setBiometricsTiming(timing: MobileUnlockTiming) {
|
||||
setMobileBiometricsTiming(timing: MobileUnlockTiming): void {
|
||||
this.storageService.setValue(StorageKey.MobileBiometricsTiming, timing, StorageValueModes.Nonwrapped)
|
||||
this.mobileBiometricsTiming = timing
|
||||
}
|
||||
|
||||
setMobilePasscodeTiming(timing: MobileUnlockTiming): void {
|
||||
this.storageService.setValue(StorageKey.MobilePasscodeTiming, timing, StorageValueModes.Nonwrapped)
|
||||
this.mobilePasscodeTiming = timing
|
||||
}
|
||||
|
||||
setMobileScreenshotPrivacyEnabled(isEnabled: boolean) {
|
||||
return this.storageService.setValue(StorageKey.MobileScreenshotPrivacyEnabled, isEnabled, StorageValueModes.Default)
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ describe('device authentication', function () {
|
||||
const passcode = 'foobar'
|
||||
const wrongPasscode = 'barfoo'
|
||||
await application.addPasscode(passcode)
|
||||
await application.protectionService.enableBiometrics()
|
||||
await application.protections.enableBiometrics()
|
||||
expect(await application.hasPasscode()).to.equal(true)
|
||||
expect((await application.protectionService.createLaunchChallenge()).prompts.length).to.equal(2)
|
||||
expect(application.protocolService.rootKeyEncryption.keyMode).to.equal(KeyMode.WrapperOnly)
|
||||
|
||||
@@ -311,7 +311,7 @@ describe('protections', function () {
|
||||
|
||||
it('no account, no passcode, biometrics', async function () {
|
||||
application = await Factory.createInitAppWithFakeCrypto()
|
||||
await application.enableBiometrics()
|
||||
await application.protections.enableBiometrics()
|
||||
expect(application.hasProtectionSources()).to.be.true
|
||||
})
|
||||
|
||||
@@ -324,7 +324,7 @@ describe('protections', function () {
|
||||
it('no account, passcode, biometrics', async function () {
|
||||
application = await Factory.createInitAppWithFakeCrypto()
|
||||
await application.addPasscode('passcode')
|
||||
await application.enableBiometrics()
|
||||
await application.protections.enableBiometrics()
|
||||
expect(application.hasProtectionSources()).to.be.true
|
||||
})
|
||||
|
||||
@@ -345,7 +345,7 @@ describe('protections', function () {
|
||||
email: UuidGenerator.GenerateUuid(),
|
||||
password: UuidGenerator.GenerateUuid(),
|
||||
})
|
||||
await application.enableBiometrics()
|
||||
await application.protections.enableBiometrics()
|
||||
expect(application.hasProtectionSources()).to.be.true
|
||||
})
|
||||
|
||||
@@ -372,7 +372,7 @@ describe('protections', function () {
|
||||
})
|
||||
Factory.handlePasswordChallenges(application, password)
|
||||
await application.addPasscode('passcode')
|
||||
await application.enableBiometrics()
|
||||
await application.protections.enableBiometrics()
|
||||
expect(application.hasProtectionSources()).to.be.true
|
||||
})
|
||||
})
|
||||
|
||||
@@ -32,7 +32,7 @@ import { PrefDefaults } from '@/Constants/PrefDefaults'
|
||||
type WebServices = {
|
||||
viewControllerManager: ViewControllerManager
|
||||
desktopService?: DesktopManager
|
||||
autolockService: AutolockService
|
||||
autolockService?: AutolockService
|
||||
archiveService: ArchiveManager
|
||||
themeService: ThemeManager
|
||||
io: IOService
|
||||
@@ -237,7 +237,7 @@ export class WebApplication extends SNApplication implements WebApplicationInter
|
||||
async handleMobileGainingFocusEvent(): Promise<void> {}
|
||||
|
||||
async handleMobileLosingFocusEvent(): Promise<void> {
|
||||
if (this.getMobileScreenshotPrivacyEnabled()) {
|
||||
if (this.protections.getMobileScreenshotPrivacyEnabled()) {
|
||||
this.mobileDevice().stopHidingMobileInterfaceFromScreenshots()
|
||||
}
|
||||
|
||||
@@ -245,7 +245,7 @@ export class WebApplication extends SNApplication implements WebApplicationInter
|
||||
}
|
||||
|
||||
async handleMobileResumingFromBackgroundEvent(): Promise<void> {
|
||||
if (this.getMobileScreenshotPrivacyEnabled()) {
|
||||
if (this.protections.getMobileScreenshotPrivacyEnabled()) {
|
||||
this.mobileDevice().hideMobileInterfaceFromScreenshots()
|
||||
}
|
||||
}
|
||||
@@ -256,10 +256,10 @@ export class WebApplication extends SNApplication implements WebApplicationInter
|
||||
return
|
||||
}
|
||||
|
||||
const hasBiometrics = this.hasBiometrics()
|
||||
const hasBiometrics = this.protections.hasBiometricsEnabled()
|
||||
const hasPasscode = this.hasPasscode()
|
||||
const passcodeTiming = await this.getMobilePasscodeTiming()
|
||||
const biometricsTiming = await this.getMobileBiometricsTiming()
|
||||
const passcodeTiming = this.protections.getMobilePasscodeTiming()
|
||||
const biometricsTiming = this.protections.getMobileBiometricsTiming()
|
||||
|
||||
const passcodeLockImmediately = hasPasscode && passcodeTiming === MobileUnlockTiming.Immediately
|
||||
const biometricsLockImmediately = hasBiometrics && biometricsTiming === MobileUnlockTiming.Immediately
|
||||
|
||||
@@ -34,7 +34,6 @@ const createApplication = (
|
||||
const archiveService = new ArchiveManager(application)
|
||||
const io = new IOService(platform === Platform.MacWeb || platform === Platform.MacDesktop)
|
||||
const internalEventBus = new InternalEventBus()
|
||||
const autolockService = new AutolockService(application, internalEventBus)
|
||||
const themeService = new ThemeManager(application, internalEventBus)
|
||||
|
||||
application.setWebServices({
|
||||
@@ -42,7 +41,7 @@ const createApplication = (
|
||||
archiveService,
|
||||
desktopService: isDesktopDevice(device) ? new DesktopManager(application, device) : undefined,
|
||||
io,
|
||||
autolockService,
|
||||
autolockService: application.isNativeMobileWeb() ? undefined : new AutolockService(application, internalEventBus),
|
||||
themeService,
|
||||
})
|
||||
|
||||
|
||||
@@ -16,10 +16,12 @@ type Props = {
|
||||
const BiometricsLock = ({ application }: Props) => {
|
||||
const [hasBiometrics, setHasBiometrics] = useState(false)
|
||||
const [supportsBiometrics, setSupportsBiometrics] = useState(false)
|
||||
const [biometricsTimingOptions, setBiometricsTimingOptions] = useState(() => application.getBiometricsTimingOptions())
|
||||
const [biometricsTimingOptions, setBiometricsTimingOptions] = useState(() =>
|
||||
application.protections.getMobileBiometricsTimingOptions(),
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
const appHasBiometrics = application.hasBiometrics()
|
||||
const appHasBiometrics = application.protections.hasBiometricsEnabled()
|
||||
setHasBiometrics(appHasBiometrics)
|
||||
|
||||
const hasBiometricsSupport = async () => {
|
||||
@@ -32,12 +34,12 @@ const BiometricsLock = ({ application }: Props) => {
|
||||
}, [application])
|
||||
|
||||
const setBiometricsTimingValue = async (timing: MobileUnlockTiming) => {
|
||||
await application.setBiometricsTiming(timing)
|
||||
setBiometricsTimingOptions(() => application.getBiometricsTimingOptions())
|
||||
application.protections.setMobileBiometricsTiming(timing)
|
||||
setBiometricsTimingOptions(() => application.protections.getMobileBiometricsTimingOptions())
|
||||
}
|
||||
|
||||
const disableBiometrics = useCallback(async () => {
|
||||
if (await application.disableBiometrics()) {
|
||||
if (await application.protections.disableBiometrics()) {
|
||||
setHasBiometrics(false)
|
||||
}
|
||||
}, [application])
|
||||
@@ -47,7 +49,7 @@ const BiometricsLock = ({ application }: Props) => {
|
||||
await disableBiometrics()
|
||||
} else {
|
||||
setHasBiometrics(true)
|
||||
application.enableBiometrics()
|
||||
application.protections.enableBiometrics()
|
||||
await setBiometricsTimingValue(MobileUnlockTiming.OnQuit)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ const MultitaskingPrivacy = ({ application }: Props) => {
|
||||
const [hasScreenshotPrivacy, setHasScreenshotPrivacy] = useState<boolean>(false)
|
||||
|
||||
useEffect(() => {
|
||||
const hasScreenshotPrivacyEnabled = application.getMobileScreenshotPrivacyEnabled()
|
||||
const hasScreenshotPrivacyEnabled = application.protections.getMobileScreenshotPrivacyEnabled()
|
||||
setHasScreenshotPrivacy(hasScreenshotPrivacyEnabled)
|
||||
}, [application])
|
||||
|
||||
@@ -24,7 +24,7 @@ const MultitaskingPrivacy = ({ application }: Props) => {
|
||||
const enable = !hasScreenshotPrivacy
|
||||
setHasScreenshotPrivacy(enable)
|
||||
|
||||
application.setMobileScreenshotPrivacyEnabled(enable)
|
||||
application.protections.setMobileScreenshotPrivacyEnabled(enable)
|
||||
;(application.deviceInterface as MobileDeviceInterface).setAndroidScreenshotPrivacy(enable)
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ import { WebApplication } from '@/Application/Application'
|
||||
import { preventRefreshing } from '@/Utils'
|
||||
import { alertDialog } from '@standardnotes/ui-services'
|
||||
import { FormEvent, useCallback, useEffect, useRef, useState } from 'react'
|
||||
import { ApplicationEvent } from '@standardnotes/snjs'
|
||||
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'
|
||||
@@ -28,8 +28,8 @@ type Props = {
|
||||
}
|
||||
|
||||
const PasscodeLock = ({ application, viewControllerManager }: Props) => {
|
||||
const isNativeMobileWeb = application.isNativeMobileWeb()
|
||||
const keyStorageInfo = StringUtils.keyStorageInfo(application)
|
||||
const passcodeAutoLockOptions = application.getAutolockService().getAutoLockIntervalOptions()
|
||||
|
||||
const { setIsEncryptionEnabled, setIsBackupEncrypted, setEncryptionStatusString } =
|
||||
viewControllerManager.accountMenuController
|
||||
@@ -44,6 +44,10 @@ const PasscodeLock = ({ application, viewControllerManager }: Props) => {
|
||||
const [canAddPasscode, setCanAddPasscode] = useState(!application.isEphemeralSession())
|
||||
const [hasPasscode, setHasPasscode] = useState(application.hasPasscode())
|
||||
|
||||
const [mobilePasscodeTimingOptions, setMobilePasscodeTimingOptions] = useState(() =>
|
||||
application.protections.getMobilePasscodeTimingOptions(),
|
||||
)
|
||||
|
||||
const handleAddPassCode = () => {
|
||||
setShowPasscodeForm(true)
|
||||
setIsPasscodeFocused(true)
|
||||
@@ -53,8 +57,8 @@ const PasscodeLock = ({ application, viewControllerManager }: Props) => {
|
||||
handleAddPassCode()
|
||||
}
|
||||
|
||||
const reloadAutoLockInterval = useCallback(async () => {
|
||||
const interval = await application.getAutolockService().getAutoLockInterval()
|
||||
const reloadDesktopAutoLockInterval = useCallback(async () => {
|
||||
const interval = await application.getAutolockService()!.getAutoLockInterval()
|
||||
setSelectedAutoLockInterval(interval)
|
||||
}, [application])
|
||||
|
||||
@@ -77,19 +81,27 @@ const PasscodeLock = ({ application, viewControllerManager }: Props) => {
|
||||
setIsBackupEncrypted(encryptionEnabled)
|
||||
}, [application, setEncryptionStatusString, setIsBackupEncrypted, setIsEncryptionEnabled])
|
||||
|
||||
const selectAutoLockInterval = async (interval: number) => {
|
||||
const selectDesktopAutoLockInterval = async (interval: number) => {
|
||||
if (!(await application.authorizeAutolockIntervalChange())) {
|
||||
return
|
||||
}
|
||||
await application.getAutolockService().setAutoLockInterval(interval)
|
||||
reloadAutoLockInterval().catch(console.error)
|
||||
|
||||
await application.getAutolockService()!.setAutoLockInterval(interval)
|
||||
reloadDesktopAutoLockInterval().catch(console.error)
|
||||
}
|
||||
|
||||
const setMobilePasscodeTiming = (timing: MobileUnlockTiming) => {
|
||||
application.protections.setMobilePasscodeTiming(timing)
|
||||
setMobilePasscodeTimingOptions(application.protections.getMobilePasscodeTimingOptions())
|
||||
}
|
||||
|
||||
const removePasscodePressed = async () => {
|
||||
await preventRefreshing(STRING_CONFIRM_APP_QUIT_DURING_PASSCODE_REMOVAL, async () => {
|
||||
if (await application.removePasscode()) {
|
||||
await application.getAutolockService().deleteAutolockPreference()
|
||||
await reloadAutoLockInterval()
|
||||
if (!isNativeMobileWeb) {
|
||||
await application.getAutolockService()?.deleteAutolockPreference()
|
||||
await reloadDesktopAutoLockInterval()
|
||||
}
|
||||
refreshEncryptionStatus()
|
||||
}
|
||||
})
|
||||
@@ -141,11 +153,11 @@ const PasscodeLock = ({ application, viewControllerManager }: Props) => {
|
||||
refreshEncryptionStatus()
|
||||
}, [refreshEncryptionStatus])
|
||||
|
||||
// `reloadAutoLockInterval` gets interval asynchronously, therefore we call `useEffect` to set initial
|
||||
// value of `selectedAutoLockInterval`
|
||||
useEffect(() => {
|
||||
reloadAutoLockInterval().catch(console.error)
|
||||
}, [reloadAutoLockInterval])
|
||||
if (!isNativeMobileWeb) {
|
||||
reloadDesktopAutoLockInterval().catch(console.error)
|
||||
}
|
||||
}, [reloadDesktopAutoLockInterval, isNativeMobileWeb])
|
||||
|
||||
useEffect(() => {
|
||||
if (isPasscodeFocused) {
|
||||
@@ -154,7 +166,6 @@ const PasscodeLock = ({ application, viewControllerManager }: Props) => {
|
||||
}
|
||||
}, [isPasscodeFocused])
|
||||
|
||||
// Add the required event observers
|
||||
useEffect(() => {
|
||||
const removeKeyStatusChangedObserver = application.addEventObserver(async () => {
|
||||
setCanAddPasscode(!application.isEphemeralSession())
|
||||
@@ -229,7 +240,7 @@ const PasscodeLock = ({ application, viewControllerManager }: Props) => {
|
||||
</PreferencesSegment>
|
||||
</PreferencesGroup>
|
||||
|
||||
{hasPasscode && (
|
||||
{hasPasscode && !isNativeMobileWeb && (
|
||||
<>
|
||||
<div className="min-h-3" />
|
||||
<PreferencesGroup>
|
||||
@@ -237,22 +248,57 @@ const PasscodeLock = ({ application, viewControllerManager }: Props) => {
|
||||
<Title>Autolock</Title>
|
||||
<Text className="mb-3">The autolock timer begins when the window or tab loses focus.</Text>
|
||||
<div className="flex flex-row items-center">
|
||||
{passcodeAutoLockOptions.map((option) => {
|
||||
return (
|
||||
<a
|
||||
key={option.value}
|
||||
className={classNames(
|
||||
'mr-3 cursor-pointer rounded',
|
||||
option.value === selectedAutoLockInterval
|
||||
? 'bg-info px-1.5 py-0.5 text-info-contrast'
|
||||
: 'text-info',
|
||||
)}
|
||||
onClick={() => selectAutoLockInterval(option.value)}
|
||||
>
|
||||
{option.label}
|
||||
</a>
|
||||
)
|
||||
})}
|
||||
{application
|
||||
.getAutolockService()!
|
||||
.getAutoLockIntervalOptions()
|
||||
.map((option) => {
|
||||
return (
|
||||
<a
|
||||
key={option.value}
|
||||
className={classNames(
|
||||
'mr-3 cursor-pointer rounded',
|
||||
option.value === selectedAutoLockInterval
|
||||
? 'bg-info px-1.5 py-0.5 text-info-contrast'
|
||||
: 'text-info',
|
||||
)}
|
||||
onClick={() => selectDesktopAutoLockInterval(option.value)}
|
||||
>
|
||||
{option.label}
|
||||
</a>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</PreferencesSegment>
|
||||
</PreferencesGroup>
|
||||
</>
|
||||
)}
|
||||
|
||||
{hasPasscode && isNativeMobileWeb && (
|
||||
<>
|
||||
<div className="min-h-3" />
|
||||
<PreferencesGroup>
|
||||
<PreferencesSegment>
|
||||
<Title>Passcode Autolock</Title>
|
||||
<div className="flex flex-row items-center">
|
||||
<div className="mt-2 flex flex-row items-center">
|
||||
<div className={'mr-3'}>Require Passcode</div>
|
||||
{mobilePasscodeTimingOptions.map((option) => {
|
||||
return (
|
||||
<a
|
||||
key={option.key}
|
||||
className={classNames(
|
||||
'mr-3 cursor-pointer rounded px-1.5 py-0.5',
|
||||
option.selected ? 'bg-info text-info-contrast' : 'text-info',
|
||||
)}
|
||||
onClick={() => {
|
||||
void setMobilePasscodeTiming(option.key as MobileUnlockTiming)
|
||||
}}
|
||||
>
|
||||
{option.title}
|
||||
</a>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</PreferencesSegment>
|
||||
</PreferencesGroup>
|
||||
|
||||
Reference in New Issue
Block a user