Skip to content

Instantly share code, notes, and snippets.

@kulmajaba
Last active September 15, 2023 06:05
Show Gist options
  • Save kulmajaba/0d8e4de4653e07221618f4a43546160c to your computer and use it in GitHub Desktop.
Save kulmajaba/0d8e4de4653e07221618f4a43546160c to your computer and use it in GitHub Desktop.
Messaging for Figma plugins using async/await
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