feat: lint codebase and run precommit hook

This commit is contained in:
Rotimi Best
2025-12-25 09:01:51 +00:00
parent 334c67f22a
commit b31ccf3037
214 changed files with 6191 additions and 2880 deletions
@@ -2,9 +2,9 @@ import type { Readable } from 'svelte/store';
import { browser } from '$app/environment';
export interface DOMProtectionCleanup {
protectionInterval?: ReturnType<typeof setInterval>;
domObserver?: MutationObserver;
cleanup: () => void;
protectionInterval?: ReturnType<typeof setInterval>;
domObserver?: MutationObserver;
cleanup: () => void;
}
/**
@@ -14,192 +14,190 @@ export interface DOMProtectionCleanup {
* @param isEmailVerified - A readable store or getter function that returns whether email is verified
* @returns Cleanup object with intervals, observers, and a cleanup function
*/
export function setupDOMProtection(
isEmailVerified: Readable<boolean> | (() => boolean | null)
): DOMProtectionCleanup {
if (!browser) {
return {
cleanup: () => {}
};
}
export function setupDOMProtection(isEmailVerified: Readable<boolean> | (() => boolean | null)): DOMProtectionCleanup {
if (!browser) {
return {
cleanup: () => {}
};
}
let protectionInterval: ReturnType<typeof setInterval> | undefined;
let domObserver: MutationObserver | undefined;
let protectionInterval: ReturnType<typeof setInterval> | undefined;
let domObserver: MutationObserver | undefined;
// Helper to get current verification status
const getIsVerified = () => {
if (typeof isEmailVerified === 'function') {
return isEmailVerified() ?? false;
}
// For stores, we need to subscribe temporarily - this is a limitation
// In practice, the component should pass a getter function
let value = false;
const unsubscribe = isEmailVerified.subscribe((v) => {
value = v ?? false;
});
unsubscribe();
return value;
};
// Helper to get current verification status
const getIsVerified = () => {
if (typeof isEmailVerified === 'function') {
return isEmailVerified() ?? false;
}
// For stores, we need to subscribe temporarily - this is a limitation
// In practice, the component should pass a getter function
let value = false;
const unsubscribe = isEmailVerified.subscribe((v) => {
value = v ?? false;
});
unsubscribe();
return value;
};
// Function to disable all interactive elements
function disableInteractions() {
if (getIsVerified()) return;
// Function to disable all interactive elements
function disableInteractions() {
if (getIsVerified()) return;
// Disable all buttons except verification-related ones
const buttons = document.querySelectorAll('button, [role="button"], a[href], input[type="submit"]');
buttons.forEach((button) => {
const el = button as HTMLElement;
// Disable all buttons except verification-related ones
const buttons = document.querySelectorAll('button, [role="button"], a[href], input[type="submit"]');
buttons.forEach((button) => {
const el = button as HTMLElement;
// Skip if already protected or is verification-related
if (
el.dataset.verificationProtected === 'true' ||
el.closest('[data-verification-modal]') ||
el.textContent?.toLowerCase().includes('verify') ||
el.textContent?.toLowerCase().includes('resend')
) {
return;
}
// Skip if already protected or is verification-related
if (
el.dataset.verificationProtected === 'true' ||
el.closest('[data-verification-modal]') ||
el.textContent?.toLowerCase().includes('verify') ||
el.textContent?.toLowerCase().includes('resend')
) {
return;
}
el.dataset.verificationProtected = 'true';
el.dataset.originalPointerEvents = el.style.pointerEvents || 'auto';
el.style.pointerEvents = 'none';
el.style.opacity = '0.5';
el.style.cursor = 'not-allowed';
el.setAttribute('title', 'Email verification required');
el.dataset.verificationProtected = 'true';
el.dataset.originalPointerEvents = el.style.pointerEvents || 'auto';
el.style.pointerEvents = 'none';
el.style.opacity = '0.5';
el.style.cursor = 'not-allowed';
el.setAttribute('title', 'Email verification required');
// Add click prevention
el.addEventListener('click', preventInteraction, true);
el.addEventListener('mousedown', preventInteraction, true);
});
// Add click prevention
el.addEventListener('click', preventInteraction, true);
el.addEventListener('mousedown', preventInteraction, true);
});
// Disable all form inputs
const inputs = document.querySelectorAll('input, textarea, select');
inputs.forEach((input) => {
const el = input as HTMLInputElement;
// Disable all form inputs
const inputs = document.querySelectorAll('input, textarea, select');
inputs.forEach((input) => {
const el = input as HTMLInputElement;
// Skip if in verification modal
if (el.closest('[data-verification-modal]')) return;
// Skip if in verification modal
if (el.closest('[data-verification-modal]')) return;
if (!el.dataset.verificationProtected) {
el.dataset.verificationProtected = 'true';
el.dataset.originalDisabled = el.disabled ? 'true' : 'false';
el.disabled = true;
el.style.opacity = '0.5';
el.addEventListener('focus', preventInteraction, true);
el.addEventListener('input', preventInteraction, true);
}
});
if (!el.dataset.verificationProtected) {
el.dataset.verificationProtected = 'true';
el.dataset.originalDisabled = el.disabled ? 'true' : 'false';
el.disabled = true;
el.style.opacity = '0.5';
el.addEventListener('focus', preventInteraction, true);
el.addEventListener('input', preventInteraction, true);
}
});
// Disable contenteditable elements
const editables = document.querySelectorAll('[contenteditable="true"]');
editables.forEach((el) => {
const element = el as HTMLElement;
if (!element.dataset.verificationProtected) {
element.dataset.verificationProtected = 'true';
element.contentEditable = 'false';
element.style.opacity = '0.5';
}
});
}
// Disable contenteditable elements
const editables = document.querySelectorAll('[contenteditable="true"]');
editables.forEach((el) => {
const element = el as HTMLElement;
if (!element.dataset.verificationProtected) {
element.dataset.verificationProtected = 'true';
element.contentEditable = 'false';
element.style.opacity = '0.5';
}
});
}
function preventInteraction(event: Event) {
if (!getIsVerified()) {
event.preventDefault();
event.stopImmediatePropagation();
function preventInteraction(event: Event) {
if (!getIsVerified()) {
event.preventDefault();
event.stopImmediatePropagation();
// Show warning
if (event.target) {
const target = event.target as HTMLElement;
target.setAttribute('title', 'Please verify your email to use this feature');
}
// Show warning
if (event.target) {
const target = event.target as HTMLElement;
target.setAttribute('title', 'Please verify your email to use this feature');
}
// Optional: Show alert
console.warn('🔒 Email verification required to interact with this element');
return false;
}
}
// Optional: Show alert
console.warn('🔒 Email verification required to interact with this element');
return false;
}
}
// Function to restore interactions when verified
function enableInteractions() {
const protectedElements = document.querySelectorAll('[data-verification-protected="true"]');
protectedElements.forEach((el) => {
const element = el as HTMLElement;
// Function to restore interactions when verified
function enableInteractions() {
const protectedElements = document.querySelectorAll('[data-verification-protected="true"]');
protectedElements.forEach((el) => {
const element = el as HTMLElement;
// Restore styles
if (element.dataset.originalPointerEvents) {
element.style.pointerEvents = element.dataset.originalPointerEvents;
} else {
element.style.pointerEvents = '';
}
element.style.opacity = '';
element.style.cursor = '';
element.removeAttribute('title');
// Restore styles
if (element.dataset.originalPointerEvents) {
element.style.pointerEvents = element.dataset.originalPointerEvents;
} else {
element.style.pointerEvents = '';
}
element.style.opacity = '';
element.style.cursor = '';
element.removeAttribute('title');
// Restore functionality for inputs
if (element.tagName === 'INPUT' || element.tagName === 'TEXTAREA' || element.tagName === 'SELECT') {
const input = element as HTMLInputElement;
input.disabled = input.dataset.originalDisabled === 'true';
}
// Restore functionality for inputs
if (element.tagName === 'INPUT' || element.tagName === 'TEXTAREA' || element.tagName === 'SELECT') {
const input = element as HTMLInputElement;
input.disabled = input.dataset.originalDisabled === 'true';
}
// Restore contenteditable
if (element.hasAttribute('contenteditable')) {
element.contentEditable = 'true';
}
// Restore contenteditable
if (element.hasAttribute('contenteditable')) {
element.contentEditable = 'true';
}
// Remove event listeners
element.removeEventListener('click', preventInteraction, true);
element.removeEventListener('mousedown', preventInteraction, true);
element.removeEventListener('focus', preventInteraction, true);
element.removeEventListener('input', preventInteraction, true);
// Remove event listeners
element.removeEventListener('click', preventInteraction, true);
element.removeEventListener('mousedown', preventInteraction, true);
element.removeEventListener('focus', preventInteraction, true);
element.removeEventListener('input', preventInteraction, true);
// Clean up data attributes
delete element.dataset.verificationProtected;
delete element.dataset.originalPointerEvents;
delete element.dataset.originalDisabled;
});
}
// Clean up data attributes
delete element.dataset.verificationProtected;
delete element.dataset.originalPointerEvents;
delete element.dataset.originalDisabled;
});
}
// Set up mutation observer to protect new elements
if (window.MutationObserver) {
domObserver = new MutationObserver((mutations) => {
let shouldReapply = false;
mutations.forEach((mutation) => {
if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {
shouldReapply = true;
}
});
// Set up mutation observer to protect new elements
if (window.MutationObserver) {
domObserver = new MutationObserver((mutations) => {
let shouldReapply = false;
mutations.forEach((mutation) => {
if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {
shouldReapply = true;
}
});
if (shouldReapply && !getIsVerified()) {
setTimeout(disableInteractions, 100);
}
});
if (shouldReapply && !getIsVerified()) {
setTimeout(disableInteractions, 100);
}
});
domObserver.observe(document.body, {
childList: true,
subtree: true
});
}
domObserver.observe(document.body, {
childList: true,
subtree: true
});
}
// Run protection every second
protectionInterval = setInterval(() => {
if (getIsVerified()) {
enableInteractions();
cleanup();
} else {
disableInteractions();
}
}, 1000);
// Run protection every second
protectionInterval = setInterval(() => {
if (getIsVerified()) {
enableInteractions();
cleanup();
} else {
disableInteractions();
}
}, 1000);
// Initial protection
if (!getIsVerified()) {
disableInteractions();
}
// Initial protection
if (!getIsVerified()) {
disableInteractions();
}
// Add global CSS to prevent any missed interactions
if (document) {
const style = document.createElement('style');
style.id = 'email-verification-protection';
style.textContent = `
// Add global CSS to prevent any missed interactions
if (document) {
const style = document.createElement('style');
style.id = 'email-verification-protection';
style.textContent = `
body:not(.email-verified) button:not([data-verification-modal] button),
body:not(.email-verified) a:not([data-verification-modal] a),
body:not(.email-verified) input:not([data-verification-modal] input),
@@ -210,45 +208,44 @@ export function setupDOMProtection(
user-select: none !important;
}
`;
document.head.appendChild(style);
}
document.head.appendChild(style);
}
function cleanup() {
if (protectionInterval) {
clearInterval(protectionInterval);
protectionInterval = undefined;
}
if (domObserver) {
domObserver.disconnect();
domObserver = undefined;
}
function cleanup() {
if (protectionInterval) {
clearInterval(protectionInterval);
protectionInterval = undefined;
}
if (domObserver) {
domObserver.disconnect();
domObserver = undefined;
}
// Clean up protection styles
if (browser) {
const style = document?.getElementById('email-verification-protection');
if (style) style.remove();
}
}
// Clean up protection styles
if (browser) {
const style = document?.getElementById('email-verification-protection');
if (style) style.remove();
}
}
return {
protectionInterval,
domObserver,
cleanup
};
return {
protectionInterval,
domObserver,
cleanup
};
}
/**
* Updates the body class based on email verification status
*/
export function updateBodyClass(isEmailVerified: boolean | null) {
if (!browser) {
return;
}
if (!browser) {
return;
}
if (isEmailVerified) {
document.body.classList.add('email-verified');
} else {
document.body.classList.remove('email-verified');
}
if (isEmailVerified) {
document.body.classList.add('email-verified');
} else {
document.body.classList.remove('email-verified');
}
}