Calendar/app/wwwroot/ts/modules/lockscreen.ts
Janus C. H. Knudsen d7f3c55a2a Restructures project with feature-based organization
Refactors project structure to support modular, feature-driven development

Introduces comprehensive language localization support
Adds menu management with role-based access control
Implements dynamic sidebar and theme switching capabilities

Enhances project scalability and maintainability
2026-01-08 15:44:11 +01:00

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();
}
}
}