Last active
February 5, 2024 11:39
-
-
Save yagudaev/016588a8b4327500d3e6331724d2e807 to your computer and use it in GitHub Desktop.
Firebase with Figma Storage Example
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
figma.ui.onmessage = async (msg, props) => { | |
if (originalOnMessage && isArray(msg)) { | |
originalOnMessage.apply(null, [msg, props]) | |
return | |
} | |
switch (msg.type) { | |
case "req-read-local-storage": | |
await readLocalStorage(msg.data) | |
// figma.closePlugin() | |
return | |
case "req-update-local-storage": | |
await updateLocalStorage(msg.data) | |
return | |
default: | |
console.error("Unknown message type", msg.type) | |
} | |
} |
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 { figmaPluginPersistence } from "./figmaPluginPersistence" | |
// Initialize Firebase auth | |
const auth = initializeAuth(firebaseApp, { | |
persistence: isLocalStorageAvailable() ? browserLocalPersistence : figmaPluginPersistence | |
}) |
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
mport { Persistence } from "firebase/auth" | |
import * as FigmaPluginAPI from "./figmaPluginAPI" | |
export const enum PersistenceType { | |
SESSION = "SESSION", | |
LOCAL = "LOCAL", | |
NONE = "NONE" | |
} | |
export type PersistedBlob = Record<string, unknown> | |
export interface Instantiator<T> { | |
(blob: PersistedBlob): T | |
} | |
export type PersistenceValue = PersistedBlob | string | |
export const STORAGE_AVAILABLE_KEY = "__sak" | |
export interface StorageEventListener { | |
(value: PersistenceValue | null): void | |
} | |
export interface PersistenceInternal extends Persistence { | |
type: PersistenceType | |
_isAvailable(): Promise<boolean> | |
_set(key: string, value: PersistenceValue): Promise<void> | |
_get<T extends PersistenceValue>(key: string): Promise<T | null> | |
_remove(key: string): Promise<void> | |
_addListener(key: string, listener: StorageEventListener): void | |
_removeListener(key: string, listener: StorageEventListener): void | |
// Should this persistence allow migration up the chosen hierarchy? | |
_shouldAllowMigration?: boolean | |
} | |
interface Storage { | |
getItem: (key: string) => Promise<string | null> | |
setItem: (key: string, value: string) => Promise<void> | |
removeItem: (key: string) => Promise<void> | |
} | |
export const figmaPluginPersistence: Persistence = getFigmaPluginPersistence({ | |
getItem: async (key: string) => { | |
const value = await FigmaPluginAPI.Storage.getItem(key) | |
return value | |
}, | |
setItem: async (key: string, value: string) => { | |
await FigmaPluginAPI.Storage.setItem(key, value) | |
}, | |
removeItem: async (key: string) => { | |
await FigmaPluginAPI.Storage.removeItem(key) | |
} | |
}) | |
export function getFigmaPluginPersistence(storage: Storage): Persistence { | |
// In the _getInstance() implementation (see src/core/persistence/index.ts), | |
// we expect each "externs.Persistence" object passed to us by the user to | |
// be able to be instantiated (as a class) using "new". That function also | |
// expects the constructor to be empty. Since ReactNativeStorage requires the | |
// underlying storage layer, we need to be able to create subclasses | |
// (closures, esentially) that have the storage layer but empty constructor. | |
return class implements PersistenceInternal { | |
static type: "LOCAL" = "LOCAL" | |
readonly type: PersistenceType = PersistenceType.LOCAL | |
async _isAvailable(): Promise<boolean> { | |
try { | |
if (!storage) { | |
return false | |
} | |
await storage.setItem(STORAGE_AVAILABLE_KEY, "1") | |
await storage.removeItem(STORAGE_AVAILABLE_KEY) | |
return true | |
} catch { | |
return false | |
} | |
} | |
_set(key: string, value: PersistenceValue): Promise<void> { | |
return storage.setItem(key, JSON.stringify(value)) | |
} | |
async _get<T extends PersistenceValue>(key: string): Promise<T | null> { | |
const json = await storage.getItem(key) | |
return json ? JSON.parse(json) : null | |
} | |
_remove(key: string): Promise<void> { | |
return storage.removeItem(key) | |
} | |
_addListener(_key: string, _listener: StorageEventListener): void { | |
// Listeners are not supported for React Native storage. | |
return | |
} | |
_removeListener(_key: string, _listener: StorageEventListener): void { | |
// Listeners are not supported for React Native storage. | |
return | |
} | |
} | |
} |
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 { emit, on, once } from "@create-figma-plugin/utilities" | |
export const Storage = { | |
getItem: async (key: string): Promise<string | null> => { | |
return new Promise((resolve, reject) => { | |
const unsubscribe = once("res-get-local-storage", ({ data }) => { | |
resolve(data) | |
unsubscribe() | |
}) | |
emit("req-get-local-storage", { key }) | |
}) | |
}, | |
setItem: async (key: string, value: string): Promise<void> => { | |
return new Promise((resolve, reject) => { | |
const unsubscribe = once("res-set-local-storage", () => { | |
resolve() | |
unsubscribe() | |
}) | |
emit("req-set-local-storage", { key, value }) | |
}) | |
}, | |
removeItem: async (key: string): Promise<void> => { | |
return new Promise((resolve, reject) => { | |
const unsubscribe = once("res-remove-local-storage", () => { | |
resolve() | |
unsubscribe() | |
}) | |
emit("req-remove-local-storage", { key }) | |
}) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
The above code makes it possible to use Firebase Auth with Figma by replacing
localStorage
withfigma.clientStorage
. It requires message passing between the UI and Main (figma sandbox running using QuickJS).This can be rolled into an NPM package for easy usage.
If you do endup doing that, please link back here 😁.