opnform-host-nginx/client/composables/useWindowMessage.js

188 lines
5.9 KiB
JavaScript
Raw Normal View History

Re-login modal (#717) * Implement quick login/register flow with global event handling - Add QuickRegister component with improved modal management - Integrate quick login/register with app store state - Implement custom event handling for login/registration flow - Update OAuth callback to support quick login in popup windows - Refactor authentication-related components to use global events * Refactor authentication flow with centralized useAuth composable - Create new useAuth composable to centralize login, registration, and social login logic - Simplify authentication methods in LoginForm and RegisterForm - Add event-based login/registration flow with quick login support - Remove redundant API calls and consolidate authentication processes - Improve error handling and analytics tracking for authentication events * Enhance QuickRegister and RegisterForm components with unauthorized error handling - Add closeable functionality to modals based on unauthorized error state - Implement logout button in QuickRegister for unauthorized users - Reset unauthorized error state on component unmount - Update styling for "OR" text in RegisterForm for consistency - Set unauthorized error flag in app store upon 401 response in API calls * Refactor Authentication Flow and Remove Unused Callback Views - Deleted unused callback views for Notion and OAuth to streamline the codebase. - Updated QuickRegister and LoginForm components to remove the after-login event emission, replacing it with a window message system for better communication between components. - Enhanced the RegisterForm and other components to utilize the new window message system for handling login completion, improving reliability and maintainability. - Added a verifyAuthentication method in the useAuth composable to ensure user data is loaded correctly after social logins, including retry logic for fetching user data. These changes aim to simplify the authentication process and improve the overall user experience by ensuring a more robust handling of login events. * Add eslint-disable comment to useWindowMessage composable for linting control * Refactor QuickRegister.vue for improved template structure and clarity - Adjusted the rendering of horizontal dividers and the "or" text for better semantic HTML. - Added a compact-header prop to the modal for enhanced layout control. These changes aim to enhance the readability and maintainability of the QuickRegister component. --------- Co-authored-by: Julien Nahum <julien@nahum.net>
2025-03-25 10:41:11 +01:00
/* eslint-disable */
/**
* Composable for handling window message communication
* Provides a consistent interface for cross-window/iframe communication
*/
// Define common message types as constants
export const WindowMessageTypes = {
LOGIN_COMPLETE: 'login-complete',
AFTER_LOGIN: 'after-login'
}
export const useWindowMessage = (messageType = null) => {
const listeners = ref(new Map())
/**
* Derives an acknowledgment message name from the original message name
* @param {string} originalMessage - The original message name
* @returns {string} - The derived acknowledgment message name
*/
const deriveAcknowledgmentName = (originalMessage) => {
return `${originalMessage}-acknowledged`
}
/**
* Add a listener for a specific message type
*
* @param {string} eventType - The type of message to listen for (defaults to constructor value)
* @param {Function} callback - The callback function to call when the message is received
* @param {Object} options - Options for the listener
* @param {boolean} options.useMessageChannel - Whether to expect and use MessageChannel ports in the event
* @param {boolean} options.acknowledge - Whether to automatically acknowledge the message
*/
const listen = (callback, options = {}, eventType = null) => {
const targetEventType = eventType || messageType
if (!targetEventType) {
console.error('No message type provided to listen for')
return
}
const {
useMessageChannel = true,
acknowledge = true
} = options
const acknowledgmentName = deriveAcknowledgmentName(targetEventType)
const handler = (event) => {
// For simple messages
if (!useMessageChannel && event.data === targetEventType) {
callback(event)
return
}
// For MessageChannel messages
if (useMessageChannel && event.data === targetEventType && event.ports && event.ports.length > 0) {
// Send acknowledgement if requested
if (acknowledge && event.ports[0]) {
event.ports[0].postMessage(acknowledgmentName)
}
// Call the callback with the event
callback(event)
return
}
}
// Add the listener to the window
window.addEventListener('message', handler)
// Store the handler for cleanup
listeners.value.set(targetEventType, handler)
// Return a function to remove the listener
return () => stopListening(targetEventType)
}
/**
* Remove a listener for a specific message type
*
* @param {string} eventType - The type of message to stop listening for
*/
const stopListening = (eventType = null) => {
const targetEventType = eventType || messageType
if (!targetEventType) {
console.error('No message type provided to stop listening for')
return
}
const handler = listeners.value.get(targetEventType)
if (handler) {
window.removeEventListener('message', handler)
listeners.value.delete(targetEventType)
}
}
/**
* Send a message to another window
*
* @param {Window} targetWindow - The window to send the message to
* @param {Object} options - Options for sending the message
* @param {string} options.eventType - The type of message to send (defaults to constructor value)
* @param {string} options.targetOrigin - The origin to send the message to, defaults to '*'
* @param {boolean} options.useMessageChannel - Whether to use MessageChannel for communication
* @param {number} options.timeout - Timeout in ms for the acknowledgment, defaults to 500ms
* @param {boolean} options.waitForAcknowledgment - Whether to wait for acknowledgment
* @returns {Promise} - Resolves when acknowledged or after timeout
*/
const send = (targetWindow, options = {}) => {
const {
eventType = null,
targetOrigin = '*',
useMessageChannel = true,
timeout = 500,
waitForAcknowledgment = true
} = options
const targetEventType = eventType || messageType
if (!targetEventType) {
console.error('No message type provided to send')
return Promise.reject(new Error('No message type provided'))
}
const acknowledgmentName = deriveAcknowledgmentName(targetEventType)
if (!useMessageChannel) {
// Simple message without MessageChannel
targetWindow.postMessage(targetEventType, targetOrigin)
return Promise.resolve()
} else {
// Using MessageChannel for two-way communication
return new Promise((resolve) => {
// Create a message channel for two-way communication
const channel = new MessageChannel()
// If we expect an acknowledgment, listen for it
if (waitForAcknowledgment) {
channel.port1.onmessage = (event) => {
if (event.data === acknowledgmentName) {
resolve(true) // Acknowledged
}
}
}
// Send the message with the port
targetWindow.postMessage(targetEventType, targetOrigin, [channel.port2])
// Set a timeout as fallback
if (waitForAcknowledgment) {
setTimeout(() => resolve(false), timeout)
} else {
resolve(true)
}
})
}
}
/**
* Cleanup all listeners
* Call this explicitly if needed, though it's automatically called on unmount
*/
const cleanup = () => {
listeners.value.forEach((handler, type) => {
window.removeEventListener('message', handler)
})
listeners.value.clear()
}
// Auto-cleanup when the component is unmounted
onUnmounted(() => {
cleanup()
})
// If messageType was provided on creation, set up a default listener
const setupDefaultListener = (callback, options = {}) => {
if (messageType && callback) {
return listen(callback, options)
}
}
return {
listen,
stopListening,
send,
cleanup,
setupDefaultListener
}
}