189 lines
6.0 KiB
JavaScript
189 lines
6.0 KiB
JavaScript
/* 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',
|
|
OAUTH_PROVIDER_CONNECTED: 'oauth-provider-connected'
|
|
}
|
|
|
|
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
|
|
}
|
|
}
|