Last active
September 15, 2023 06:05
-
-
Save kulmajaba/0d8e4de4653e07221618f4a43546160c to your computer and use it in GitHub Desktop.
Messaging for Figma plugins using async/await
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import manifest from '../manifest.json'; | |
// Types | |
// Better Omit type for Discriminated Union types | |
// https://github.com/microsoft/TypeScript/issues/31501 | |
// eslint-disable-next-line @typescript-eslint/no-explicit-any | |
export type OmitStrict<T, K extends keyof T> = T extends any ? Pick<T, Exclude<keyof T, K>> : never; | |
export enum PluginMessageType { | |
AddColor = 'ADD_COLOR', | |
Resize = 'RESIZE', | |
SaveTheme = 'SAVE_THEME', | |
GetTheme = 'GET_THEME' | |
} | |
interface PluginMessageBase { | |
type: PluginMessageType; | |
fromFigma: boolean; | |
returnId?: number; | |
} | |
interface PluginMessageSaveTheme extends PluginMessageBase { | |
type: PluginMessageType.SaveTheme; | |
payload: string; | |
fromFigma: false; | |
} | |
export interface PluginmessageGetTheme extends PluginMessageBase { | |
type: PluginMessageType.GetTheme; | |
fromFigma: false; | |
} | |
export interface PluginReturnMessageGetTheme extends PluginMessageBase { | |
type: PluginMessageType.GetTheme; | |
payload: string; | |
fromFigma: true; | |
} | |
// Discriminated Union types for all accepted messages | |
export type PluginMessage = PluginMessageSaveTheme | PluginmessageGetTheme; | |
export type PluginReturnMessage = PluginReturnMessageGetTheme; | |
// ---------------------------------------------------------------------------- | |
// plugin UI | |
let returnId = 0; | |
/** | |
* Post a message to Figma and get a response asynchronously, unless timed out | |
*/ | |
export const asyncPluginMessage = async ( | |
msg: OmitStrict<PluginMessage, 'fromFigma' | 'returnId'>, | |
timeoutMs = 5000 | |
) => { | |
console.log(`New message of type ${msg.type} with returnId ${returnId}`); | |
const result = new Promise<PluginReturnMessage>((resolve, reject) => { | |
const handleMessage = (e: MessageEvent<{ pluginMessage: PluginReturnMessage; pluginId: string }>) => { | |
if (e.data.pluginMessage.fromFigma && e.data.pluginMessage.returnId === returnId) { | |
clearTimeout(timeout); | |
window.removeEventListener('message', handleMessage); | |
console.log(`UI message for returnId ${returnId} received`); | |
console.log(e.data.pluginMessage); | |
resolve(e.data.pluginMessage); | |
} | |
}; | |
window.addEventListener('message', handleMessage); | |
const timeout = setTimeout(() => { | |
window.removeEventListener('message', handleMessage); | |
reject(`asyncMessage timeout for returnId ${returnId}`); | |
}, timeoutMs); | |
}); | |
const pluginMessage: PluginMessage = { | |
...msg, | |
fromFigma: false, | |
returnId | |
}; | |
parent.postMessage({ pluginMessage, pluginId: manifest.id }, '*'); | |
returnId += 1; | |
return result; | |
}; | |
// ---------------------------------------------------------------------------- | |
// Usage | |
export const getPluginTheme = async () => { | |
try { | |
const theme = await asyncPluginMessage({ type: PluginMessageType.GetTheme }); | |
console.log('Got theme in UI'); | |
console.log(theme); | |
} catch (e) { | |
console.log(e); | |
} | |
}; | |
// ---------------------------------------------------------------------------- | |
// Plugin logic | |
let theme = ''; | |
figma.ui.onmessage = (msg: PluginMessage) => { | |
switch (msg.type) { | |
case PluginMessageType.SaveTheme: { | |
theme = msg.payload; | |
break; | |
} | |
case PluginMessageType.GetTheme: { | |
const pluginMessage: PluginReturnMessageGetTheme = { | |
type: PluginMessageType.GetTheme, | |
payload: theme, | |
fromFigma: true, | |
returnId: msg.returnId | |
}; | |
figma.ui.postMessage(pluginMessage); | |
} | |
} | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment