/** * 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 | 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('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('swp-side-menu-action.lock') ?.addEventListener('click', () => this.show()); } private handleKeypadClick(e: Event): void { const target = e.target as HTMLElement; const key = target.closest('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(); } } }