Skip to content

Commit

Permalink
feat(plugin): persist plugin settings between sessions
Browse files Browse the repository at this point in the history
  • Loading branch information
rektdeckard committed Mar 18, 2023
1 parent 98e78f4 commit 03c50bf
Show file tree
Hide file tree
Showing 9 changed files with 175 additions and 21 deletions.
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "phosphor-figma",
"version": "2.0.1",
"version": "2.0.2",
"license": "MIT",
"homepage": "https://phosphoricons.com",
"author": {
Expand Down Expand Up @@ -45,7 +45,7 @@
},
"dependencies": {
"@phosphor-icons/core": "^2.0.2",
"@phosphor-icons/react": "^2.0.4",
"@phosphor-icons/react": "^2.0.5",
"fuse.js": "^6.6.2",
"prop-types": "^15.8.1",
"react": "^17",
Expand Down
8 changes: 6 additions & 2 deletions src/components/IconGrid/IconGrid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { useRecoilValue } from "recoil";
import { IconContext, SmileyXEyes } from "@phosphor-icons/react";

import { IconEntry } from "../../lib";
import { MessageType } from "../../types";

import {
iconWeightAtom,
Expand Down Expand Up @@ -42,7 +43,7 @@ const IconGrid: React.FC<{}> = () => {
parent.postMessage(
{
pluginMessage: {
type: "insert",
type: MessageType.INSERT,
payload: { name, pascal_name, svg, weight, flatten },
},
},
Expand Down Expand Up @@ -81,7 +82,10 @@ const IconGrid: React.FC<{}> = () => {
offset: dragStartRef.current,
};

parent.postMessage({ pluginMessage: { type: "drop", payload } }, "*");
parent.postMessage(
{ pluginMessage: { type: MessageType.DROP, payload } },
"*"
);
},
[weight, flatten]
);
Expand Down
48 changes: 41 additions & 7 deletions src/main.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
import { CUSTOM_NODE_KEY, DEFAULT_SIZE } from "./constants";
import { DropPayload, IconPayload, Message } from "./types";
import {
DropPayload,
IconPayload,
GetAsyncPayload,
SetAsyncPayload,
Message,
MessageType,
} from "./types";
import { fetchRawIcon, getInjectableNode, getOffsetVector } from "./utils";

let hasTriedDragAndDrop = false;
Expand All @@ -8,14 +15,23 @@ main();
function main() {
figma.ui.onmessage = ({ type, payload }: Message) => {
switch (type) {
case "insert":
case MessageType.INSERT:
insertIcon(payload);
break;
case "drop":
case MessageType.DROP:
hasTriedDragAndDrop = true;
dropIcon(payload);
break;
case "log":
case MessageType.STORAGE_GET_REQUEST:
getRequest(payload);
break;
case MessageType.STORAGE_SET_REQUEST:
setRequest(payload);
break;
case MessageType.STORAGE_DELETE_REQUEST:
deleteRequest(payload);
break;
case MessageType.LOG:
default:
console.log("Log: ", payload);
}
Expand All @@ -24,6 +40,24 @@ function main() {
figma.showUI(__html__, { width: 362, height: 490, themeColors: true });
}

async function getRequest(payload: GetAsyncPayload) {
const value = await figma.clientStorage.getAsync(payload.key);
if (typeof value !== "undefined") {
figma.ui.postMessage({
type: MessageType.STORAGE_GET_RESPONSE,
payload: { key: payload.key, value },
});
}
}

async function setRequest(payload: SetAsyncPayload) {
return figma.clientStorage.setAsync(payload.key, payload.value);
}

async function deleteRequest(payload: GetAsyncPayload) {
return figma.clientStorage.deleteAsync(payload.key);
}

async function insertIcon(payload: IconPayload) {
const svg = payload.flatten ? payload.svg : await fetchRawIcon(payload);

Expand All @@ -46,12 +80,12 @@ async function insertIcon(payload: IconPayload) {
injectableNode.appendChild(frame);

figma.currentPage.selection = [frame];
figma.notify(`✅ Added ${payload.pascal_name}`, { timeout: 2000 });
figma.notify(`Inserted ${payload.pascal_name}`, { timeout: 2000 });

if (!hasTriedDragAndDrop) {
setTimeout(() => {
if (!hasTriedDragAndDrop)
figma.notify("💡 Try drag-and-drop too!", { timeout: 4000 });
figma.notify("Try drag-and-drop too!", { timeout: 4000 });
hasTriedDragAndDrop = true;
}, 4000);
}
Expand Down Expand Up @@ -81,7 +115,7 @@ async function dropIcon(payload: DropPayload) {
frame.y = bounds.y + yFromCanvas / zoom - offset.y;

figma.currentPage.selection = [frame];
figma.notify(`✅ Added ${pascal_name}`, { timeout: 2000 });
figma.notify(`Inserted ${pascal_name}`, { timeout: 2000 });
}

function ungroup(node: SceneNode, parent: BaseNode & ChildrenMixin) {
Expand Down
78 changes: 78 additions & 0 deletions src/state/StorageProxy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import { AtomEffect } from "recoil";

import {
GetAsyncPayload,
SetAsyncPayload,
Response,
MessageType,
} from "../types";

type StorageListener = (event: MessageEvent) => void;

export default class StorageProxy {
static listeners: Record<string, StorageListener> = {};

static register<T>({ node, setSelf, onSet }: Parameters<AtomEffect<T>>[0]) {
const listener: StorageListener = (event) => {
const { type, payload } = event.data.pluginMessage as Response<T>;
if (type !== MessageType.STORAGE_GET_RESPONSE || payload.key !== node.key)
return;
setSelf(payload.value);
};

window.addEventListener("message", listener);

onSet((value, _, isReset) => {
if (isReset) {
StorageProxy.requestReset({ key: node.key });
} else {
StorageProxy.requestSet({ key: node.key, value });
}
});

StorageProxy.listeners[node.key] = listener;

StorageProxy.requestGet({ key: node.key });
}

static unregister(key: string) {
window.removeEventListener("message", StorageProxy.listeners[key]);
delete StorageProxy.listeners.key;
}

static requestGet(payload: GetAsyncPayload) {
parent.postMessage(
{
pluginMessage: {
type: MessageType.STORAGE_GET_REQUEST,
payload,
},
},
"*"
);
}

static requestSet<T>(payload: SetAsyncPayload<T>) {
parent.postMessage(
{
pluginMessage: {
type: MessageType.STORAGE_SET_REQUEST,
payload,
},
},
"*"
);
}

static requestReset(payload: GetAsyncPayload) {
parent.postMessage(
{
pluginMessage: {
type: MessageType.STORAGE_DELETE_REQUEST,
payload,
},
},
"*"
);
}
}
3 changes: 3 additions & 0 deletions src/state/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import Fuse from "fuse.js";

import { IconEntry } from "../lib";
import { icons } from "../lib/icons";
import StorageProxy from "./StorageProxy";

const fuse = new Fuse(icons, {
keys: [
Expand All @@ -20,6 +21,7 @@ const fuse = new Fuse(icons, {
export const iconWeightAtom = atom<IconStyle>({
key: "iconWeightAtom",
default: IconStyle.REGULAR,
effects: [StorageProxy.register],
});

export const searchQueryAtom = atom<string>({
Expand All @@ -30,6 +32,7 @@ export const searchQueryAtom = atom<string>({
export const flattenAtom = atom<boolean>({
key: "flattenAtom",
default: true,
effects: [StorageProxy.register],
});

export const filteredQueryResultsSelector = selector<ReadonlyArray<IconEntry>>({
Expand Down
43 changes: 39 additions & 4 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,55 @@ export interface DropPayload extends IconPayload {
windowSize: { width: number; height: number };
}

export type Message =
export interface GetAsyncPayload {
key: string;
}

export interface SetAsyncPayload<T = any> extends GetAsyncPayload {
value: T;
}

export enum MessageType {
INSERT = "icon_insert",
DROP = "icon_drop",
STORAGE_GET_REQUEST = "storage_get_req",
STORAGE_SET_REQUEST = "storage_set_req",
STORAGE_DELETE_REQUEST = "storage_delete_req",
STORAGE_GET_RESPONSE = "storage_get_res",
LOG = "log",
}

export type Message<T = any> =
| {
type: "insert";
type: MessageType.INSERT;
payload: IconPayload;
}
| {
type: "drop";
type: MessageType.DROP;
payload: DropPayload;
}
| {
type: "log";
type: MessageType.STORAGE_GET_REQUEST;
payload: GetAsyncPayload;
}
| {
type: MessageType.STORAGE_SET_REQUEST;
payload: SetAsyncPayload<T>;
}
| {
type: MessageType.STORAGE_DELETE_REQUEST;
payload: GetAsyncPayload;
}
| {
type: MessageType.LOG;
payload?: any;
};

export type Response<T> = {
type: MessageType.STORAGE_GET_RESPONSE;
payload: SetAsyncPayload<T>;
};

export type InjectableNode =
| PageNode
| FrameNode
Expand Down
2 changes: 1 addition & 1 deletion src/ui.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const App: React.FC<{}> = () => {
<RecoilRoot>
<div className="app">
<Toolbar />
<Suspense fallback={<p>Loading...</p>}>
<Suspense fallback={null}>
<IconGrid />
</Suspense>
<Footer />
Expand Down
2 changes: 1 addition & 1 deletion src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,6 @@ export async function fetchRawIcon(payload: IconPayload): Promise<string> {

return text;
} catch (_) {
figma.notify("Oops! Looks like you're offline.");
figma.notify("Oops! Looks like you're offline.");
}
}
8 changes: 4 additions & 4 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,10 @@
resolved "https://registry.yarnpkg.com/@phosphor-icons/core/-/core-2.0.2.tgz#108c0e5d798bcb76951ae8d0a61987cd549ac949"
integrity sha512-ZHvHBagars5C2IGKW9TdiOe8LwIVq6/LmJRfxBlfFcbsNAfYXTNzf6IUQKy0hrPTn6bTdQh5ghe436i+W1JKrw==

"@phosphor-icons/react@^2.0.4":
version "2.0.4"
resolved "https://registry.yarnpkg.com/@phosphor-icons/react/-/react-2.0.4.tgz#33a9b3feaae0031982e9ebd232448879c547190d"
integrity sha512-43GECcZKPyxFJhpXa9H9U7HXrneR4IxPtXGfUhVehvFE0kQaxI9oyVw4jePsVSn60L9e3WblFplFgGVP/9STdg==
"@phosphor-icons/react@^2.0.5":
version "2.0.5"
resolved "https://registry.yarnpkg.com/@phosphor-icons/react/-/react-2.0.5.tgz#3c07191a4489ed5a8c35f4bc228938990d13b1a4"
integrity sha512-Ghohi+Dk+Y0RoI1vra3jH2QC3sO6C16a/Km0gfy05ONIsC62Vwn1SK2Qaim77VPyn1FS7qjMxocdi9kx6btvXg==

"@types/html-minifier-terser@^5.0.0":
version "5.1.2"
Expand Down

0 comments on commit 03c50bf

Please sign in to comment.