Files
Prism-Notes-Main/tools/user-email-lookup/index.js
T

152 lines
5.1 KiB
JavaScript

/**
* User Email Lookup Tool
* Provides hover tooltips showing email addresses when hovering over user names
*
* @module UserEmailLookup
* @description Standalone utility for looking up email addresses from the Associations
* collection and displaying them as hover tooltips. Integrates with note/conversation
* sharing UI to enhance user experience when managing shared access.
* @usage Call window.setupUserEmailLookup(pbInstance, containerSelector) after page load.
* Pass PocketBase instance and CSS selector for user capsule container.
* @dependencies PocketBase instance (passed in). Browser APIs: DOM, tooltips via title attribute.
* @integration Used in index.html note/conversation sharing forms to show email on hover
* @gotchas Associations collection must have first_name field for lookups. Lookups are
* performed on hover, so network latency may cause brief delay before tooltip appears.
* Cache email lookups to minimize PocketBase queries.
*/
// Cache for email lookups to minimize queries
const emailLookupCache = {};
/**
* Fetch email from Associations collection by first name
* Uses cache to avoid duplicate queries
*
* @param {PocketBase} pb - PocketBase instance
* @param {string} firstName - First name to look up
* @returns {Promise<string>} Email address or fallback message
* @example
* const email = await getEmailByFirstName(pb, 'John');
* console.log(email); // 'john@example.com' or 'Email not found'
*/
async function getEmailByFirstName(pb, firstName) {
if (!firstName) return 'Email not found';
// Check cache first
if (emailLookupCache[firstName]) {
return emailLookupCache[firstName];
}
try {
const records = await pb.collection('Associations').getList(1, 50, {
filter: `first_name = "${firstName}"`,
});
if (records.items.length === 0) {
emailLookupCache[firstName] = 'Email not found';
return 'Email not found';
}
// Use first match's email
const email = records.items[0].email || 'Email not found';
emailLookupCache[firstName] = email;
return email;
} catch (error) {
console.error(`Failed to lookup email for "${firstName}":`, error);
return 'Error loading email';
}
}
/**
* Setup email lookups for user capsules in a container
* Attaches hover event listeners to show email in tooltip
*
* @param {PocketBase} pb - PocketBase instance
* @param {string} containerSelector - CSS selector for container with user capsules
* @example
* window.setupUserEmailLookup(pb, '#selectedUsersContainer');
*/
window.setupUserEmailLookup = function(pb, containerSelector) {
const container = document.querySelector(containerSelector);
if (!container) {
console.warn(`setupUserEmailLookup: Container not found: ${containerSelector}`);
return;
}
console.log(`✓ User Email Lookup initialized for: ${containerSelector}`);
// Observer: Watch for new capsule additions
const observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
mutation.addedNodes.forEach((node) => {
if (node.nodeType === 1 && node.classList?.contains('inline-flex')) {
attachEmailLookupTooltip(pb, node);
}
});
});
});
observer.observe(container, {
childList: true,
subtree: true
});
// Also process any existing capsules
container.querySelectorAll('.inline-flex.items-center.gap-2').forEach((capsule) => {
attachEmailLookupTooltip(pb, capsule);
});
};
/**
* Attach email lookup tooltip to a single user capsule
* Shows email on hover (with caching to prevent redundant queries)
*
* @param {PocketBase} pb - PocketBase instance
* @param {HTMLElement} capsuleEl - The user capsule element
* @private
*/
function attachEmailLookupTooltip(pb, capsuleEl) {
// Extract first name from capsule text
const nameSpan = capsuleEl.querySelector('span:not([data-email-loaded])');
if (!nameSpan || nameSpan.hasAttribute('data-email-loaded')) {
return;
}
const firstName = nameSpan.textContent.trim();
if (!firstName) return;
// Mark as processed
nameSpan.setAttribute('data-email-loaded', 'true');
// Attach hover listener
capsuleEl.addEventListener('mouseenter', async () => {
// If email already in title, don't query again
if (capsuleEl.title && !capsuleEl.title.startsWith('Loading')) {
return;
}
capsuleEl.title = 'Loading email...';
const email = await getEmailByFirstName(pb, firstName);
capsuleEl.title = email;
capsuleEl.classList.add('cursor-help');
});
capsuleEl.addEventListener('mouseleave', () => {
// Title persists so user can still see it if they hover back quickly
});
}
/**
* Clear the email lookup cache
* Useful if Associations collection is updated and you need fresh data
*
* @example
* window.clearEmailLookupCache();
*/
window.clearEmailLookupCache = function() {
Object.keys(emailLookupCache).forEach(key => delete emailLookupCache[key]);
console.log('✓ Email lookup cache cleared');
};
console.log('✓ User Email Lookup tool loaded - call window.setupUserEmailLookup(pb, containerSelector)');