182 lines
4.5 KiB
TypeScript
182 lines
4.5 KiB
TypeScript
/**
|
|
* Lock Screen Controller
|
|
*
|
|
* Handles PIN-based lock screen functionality
|
|
*/
|
|
|
|
import { DrawerController } from './drawers';
|
|
|
|
export class LockScreenController {
|
|
private static readonly CORRECT_PIN = '1234'; // Demo PIN
|
|
|
|
private lockScreen: HTMLElement | null = null;
|
|
private pinInput: HTMLElement | null = null;
|
|
private pinKeypad: HTMLElement | null = null;
|
|
private lockTimeEl: HTMLElement | null = null;
|
|
private pinDigits: NodeListOf<HTMLElement> | null = null;
|
|
private currentPin = '';
|
|
private drawers: DrawerController | null = null;
|
|
|
|
constructor(drawers?: DrawerController) {
|
|
this.drawers = drawers ?? null;
|
|
this.lockScreen = document.getElementById('lockScreen');
|
|
this.pinInput = document.getElementById('pinInput');
|
|
this.pinKeypad = document.getElementById('pinKeypad');
|
|
this.lockTimeEl = document.getElementById('lockTime');
|
|
this.pinDigits = this.pinInput?.querySelectorAll<HTMLElement>('swp-pin-digit') ?? null;
|
|
|
|
this.setupListeners();
|
|
}
|
|
|
|
/**
|
|
* Check if lock screen is active
|
|
*/
|
|
get isActive(): boolean {
|
|
return this.lockScreen?.classList.contains('active') ?? false;
|
|
}
|
|
|
|
/**
|
|
* Show the lock screen
|
|
*/
|
|
show(): void {
|
|
this.drawers?.closeAll();
|
|
|
|
if (this.lockScreen) {
|
|
this.lockScreen.classList.add('active');
|
|
document.body.style.overflow = 'hidden';
|
|
}
|
|
|
|
this.currentPin = '';
|
|
this.updateDisplay();
|
|
|
|
// Update lock time
|
|
if (this.lockTimeEl) {
|
|
this.lockTimeEl.textContent = `Låst kl. ${this.formatTime()}`;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Hide the lock screen
|
|
*/
|
|
hide(): void {
|
|
if (this.lockScreen) {
|
|
this.lockScreen.classList.remove('active');
|
|
document.body.style.overflow = '';
|
|
}
|
|
|
|
this.currentPin = '';
|
|
this.updateDisplay();
|
|
}
|
|
|
|
private formatTime(): string {
|
|
const now = new Date();
|
|
const hours = now.getHours().toString().padStart(2, '0');
|
|
const minutes = now.getMinutes().toString().padStart(2, '0');
|
|
return `${hours}:${minutes}`;
|
|
}
|
|
|
|
private updateDisplay(): void {
|
|
if (!this.pinDigits) return;
|
|
|
|
this.pinDigits.forEach((digit, index) => {
|
|
digit.classList.remove('filled', 'error');
|
|
if (index < this.currentPin.length) {
|
|
digit.textContent = '•';
|
|
digit.classList.add('filled');
|
|
} else {
|
|
digit.textContent = '';
|
|
}
|
|
});
|
|
}
|
|
|
|
private showError(): void {
|
|
if (!this.pinDigits) return;
|
|
|
|
this.pinDigits.forEach(digit => digit.classList.add('error'));
|
|
|
|
// Shake animation
|
|
this.pinInput?.classList.add('shake');
|
|
|
|
setTimeout(() => {
|
|
this.currentPin = '';
|
|
this.updateDisplay();
|
|
this.pinInput?.classList.remove('shake');
|
|
}, 500);
|
|
}
|
|
|
|
private verify(): void {
|
|
if (this.currentPin === LockScreenController.CORRECT_PIN) {
|
|
this.hide();
|
|
} else {
|
|
this.showError();
|
|
}
|
|
}
|
|
|
|
private addDigit(digit: string): void {
|
|
if (this.currentPin.length >= 4) return;
|
|
|
|
this.currentPin += digit;
|
|
this.updateDisplay();
|
|
|
|
// Auto-verify when 4 digits entered
|
|
if (this.currentPin.length === 4) {
|
|
setTimeout(() => this.verify(), 200);
|
|
}
|
|
}
|
|
|
|
private removeDigit(): void {
|
|
if (this.currentPin.length === 0) return;
|
|
this.currentPin = this.currentPin.slice(0, -1);
|
|
this.updateDisplay();
|
|
}
|
|
|
|
private clearPin(): void {
|
|
this.currentPin = '';
|
|
this.updateDisplay();
|
|
}
|
|
|
|
private setupListeners(): void {
|
|
// Keypad click handler
|
|
this.pinKeypad?.addEventListener('click', (e) => this.handleKeypadClick(e));
|
|
|
|
// Keyboard input
|
|
document.addEventListener('keydown', (e) => this.handleKeyboard(e));
|
|
|
|
// Lock button in sidebar
|
|
document.querySelector<HTMLElement>('swp-side-menu-action.lock')
|
|
?.addEventListener('click', () => this.show());
|
|
}
|
|
|
|
private handleKeypadClick(e: Event): void {
|
|
const target = e.target as HTMLElement;
|
|
const key = target.closest<HTMLElement>('swp-pin-key');
|
|
|
|
if (!key) return;
|
|
|
|
const digit = key.dataset.digit;
|
|
const action = key.dataset.action;
|
|
|
|
if (digit) {
|
|
this.addDigit(digit);
|
|
} else if (action === 'backspace') {
|
|
this.removeDigit();
|
|
} else if (action === 'clear') {
|
|
this.clearPin();
|
|
}
|
|
}
|
|
|
|
private handleKeyboard(e: KeyboardEvent): void {
|
|
if (!this.isActive) return;
|
|
|
|
// Prevent default to avoid other interactions
|
|
e.preventDefault();
|
|
|
|
if (e.key >= '0' && e.key <= '9') {
|
|
this.addDigit(e.key);
|
|
} else if (e.key === 'Backspace') {
|
|
this.removeDigit();
|
|
} else if (e.key === 'Escape') {
|
|
this.clearPin();
|
|
}
|
|
}
|
|
}
|