feat(web): extract ui-services package
This commit is contained in:
129
packages/ui-services/src/Security/AutolockService.ts
Normal file
129
packages/ui-services/src/Security/AutolockService.ts
Normal file
@@ -0,0 +1,129 @@
|
||||
import { ApplicationService } from '@standardnotes/snjs'
|
||||
|
||||
const MILLISECONDS_PER_SECOND = 1000
|
||||
const POLL_INTERVAL = 50
|
||||
|
||||
const LockInterval = {
|
||||
None: 0,
|
||||
Immediate: 1,
|
||||
OneMinute: 60 * MILLISECONDS_PER_SECOND,
|
||||
FiveMinutes: 300 * MILLISECONDS_PER_SECOND,
|
||||
OneHour: 3600 * MILLISECONDS_PER_SECOND,
|
||||
}
|
||||
|
||||
const STORAGE_KEY_AUTOLOCK_INTERVAL = 'AutoLockIntervalKey'
|
||||
|
||||
export class AutolockService extends ApplicationService {
|
||||
private pollInterval: any
|
||||
private lastFocusState?: 'hidden' | 'visible'
|
||||
private lockAfterDate?: Date
|
||||
|
||||
override onAppLaunch() {
|
||||
this.beginPolling()
|
||||
return super.onAppLaunch()
|
||||
}
|
||||
|
||||
override deinit() {
|
||||
this.cancelAutoLockTimer()
|
||||
if (this.pollInterval) {
|
||||
clearInterval(this.pollInterval)
|
||||
}
|
||||
super.deinit()
|
||||
}
|
||||
|
||||
private lockApplication() {
|
||||
if (!this.application.hasPasscode()) {
|
||||
throw Error('Attempting to lock application with no passcode')
|
||||
}
|
||||
this.application.lock().catch(console.error)
|
||||
}
|
||||
|
||||
async setAutoLockInterval(interval: number) {
|
||||
return this.application.setValue(STORAGE_KEY_AUTOLOCK_INTERVAL, interval)
|
||||
}
|
||||
|
||||
async getAutoLockInterval() {
|
||||
const interval = (await this.application.getValue(STORAGE_KEY_AUTOLOCK_INTERVAL)) as number
|
||||
if (interval) {
|
||||
return interval
|
||||
} else {
|
||||
return LockInterval.None
|
||||
}
|
||||
}
|
||||
|
||||
async deleteAutolockPreference() {
|
||||
await this.application.removeValue(STORAGE_KEY_AUTOLOCK_INTERVAL)
|
||||
this.cancelAutoLockTimer()
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify document is in focus every so often as visibilitychange event is
|
||||
* not triggered on a typical window blur event but rather on tab changes.
|
||||
*/
|
||||
beginPolling() {
|
||||
this.pollInterval = setInterval(async () => {
|
||||
const locked = await this.application.isLocked()
|
||||
if (!locked && this.lockAfterDate && new Date() > this.lockAfterDate) {
|
||||
this.lockApplication()
|
||||
}
|
||||
const hasFocus = document.hasFocus()
|
||||
if (hasFocus && this.lastFocusState === 'hidden') {
|
||||
this.documentVisibilityChanged(true).catch(console.error)
|
||||
} else if (!hasFocus && this.lastFocusState === 'visible') {
|
||||
this.documentVisibilityChanged(false).catch(console.error)
|
||||
}
|
||||
/* Save this to compare against next time around */
|
||||
this.lastFocusState = hasFocus ? 'visible' : 'hidden'
|
||||
}, POLL_INTERVAL)
|
||||
}
|
||||
|
||||
getAutoLockIntervalOptions() {
|
||||
return [
|
||||
{
|
||||
value: LockInterval.None,
|
||||
label: 'Off',
|
||||
},
|
||||
{
|
||||
value: LockInterval.Immediate,
|
||||
label: 'Immediately',
|
||||
},
|
||||
{
|
||||
value: LockInterval.OneMinute,
|
||||
label: '1m',
|
||||
},
|
||||
{
|
||||
value: LockInterval.FiveMinutes,
|
||||
label: '5m',
|
||||
},
|
||||
{
|
||||
value: LockInterval.OneHour,
|
||||
label: '1h',
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
async documentVisibilityChanged(visible: boolean) {
|
||||
if (visible) {
|
||||
this.cancelAutoLockTimer()
|
||||
} else {
|
||||
this.beginAutoLockTimer().catch(console.error)
|
||||
}
|
||||
}
|
||||
|
||||
async beginAutoLockTimer() {
|
||||
const interval = await this.getAutoLockInterval()
|
||||
if (interval === LockInterval.None) {
|
||||
return
|
||||
}
|
||||
const addToNow = (seconds: number) => {
|
||||
const date = new Date()
|
||||
date.setSeconds(date.getSeconds() + seconds)
|
||||
return date
|
||||
}
|
||||
this.lockAfterDate = addToNow(interval / MILLISECONDS_PER_SECOND)
|
||||
}
|
||||
|
||||
cancelAutoLockTimer() {
|
||||
this.lockAfterDate = undefined
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user