Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] feat: add current user avatar badge to the Cover page* #3

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions manifest.json
Expand Up @@ -3,5 +3,6 @@
"id": "1139148181170685187",
"api": "1.0.0",
"main": "dist/figmaEntrypoint.js",
"permissions": ["currentuser"],
"editorType": ["figma"]
}
7 changes: 6 additions & 1 deletion src/commands-setup/CommandsMapping.ts
@@ -1,12 +1,17 @@
import { NetworkRequestCommandHandler } from "../browser-commands/network-request/NetworkRequestCommandHandler";
import { CreatePagesCommandHandler } from "../scene-commands/create-pages/CreatePagesCommandHandler";
import { CurrentUserAvatarBadgeCreator } from "../scene-commands/create-pages/CurrentUserAvatarBadgeCreator";
import { Command } from "./Command";
import { CommandHandler } from "./CommandHandler";

// 👋 Add below your new commands.
// Define its arbitrary key and its corresponding Handler class.
// Tip: Declare your Command and CommandHandler classes creating a folder inside the `src/scene-commands` or `src/browser-commands` ones depending on the things you need to get access to (see the README explanation) 😊

const currentUserAvatarBadgeCreator = new CurrentUserAvatarBadgeCreator(figma);

export const CommandsMapping: Record<string, () => CommandHandler<Command>> = {
networkRequest: () => new NetworkRequestCommandHandler(),
createPages: () => new CreatePagesCommandHandler(figma),
createPages: () =>
new CreatePagesCommandHandler(currentUserAvatarBadgeCreator, figma),
};
46 changes: 39 additions & 7 deletions src/scene-commands/create-pages/CreatePagesCommandHandler.ts
@@ -1,10 +1,24 @@
import { CommandHandler } from "../../commands-setup/CommandHandler";
import { CreatePagesCommand } from "./CreatePagesCommand";
import {
BadgeStyle,
CurrentUserAvatarBadgeCreator,
} from "./CurrentUserAvatarBadgeCreator";

export class CreatePagesCommandHandler
implements CommandHandler<CreatePagesCommand>
{
constructor(private readonly figma: PluginAPI) {}
private fontFamily = "Moderat";

private frameDimensions = {
width: 1240,
height: 640,
};

constructor(
private readonly currentUserAvatarBadgeCreator: CurrentUserAvatarBadgeCreator,
private readonly figma: PluginAPI
) {}

// `command` argument needed due to polymorphism.
// eslint-disable-next-line @typescript-eslint/no-unused-vars
Expand Down Expand Up @@ -47,7 +61,7 @@ export class CreatePagesCommandHandler
frame.counterAxisAlignItems = "CENTER";
frame.paddingTop = 100;
frame.itemSpacing = 30;
frame.resize(1240, 640);
frame.resize(this.frameDimensions.width, this.frameDimensions.height);

return frame;
};
Expand All @@ -58,7 +72,7 @@ export class CreatePagesCommandHandler
): Promise<TextNode> => {
const heading = this.figma.createText();

const font = { family: "Moderat", style: "Bold" };
const font = { family: this.fontFamily, style: "Bold" };
await this.figma.loadFontAsync(font);
heading.fontName = font;
heading.characters = name;
Expand All @@ -79,7 +93,7 @@ export class CreatePagesCommandHandler
): Promise<TextNode> => {
const description = this.figma.createText();

const font = { family: "Moderat", style: "Regular" };
const font = { family: this.fontFamily, style: "Regular" };
await this.figma.loadFontAsync(font);
description.fontName = font;
description.fontSize = 64;
Expand All @@ -92,18 +106,36 @@ export class CreatePagesCommandHandler

return description;
};

const frame = await createFrame();
this.figma.currentPage.appendChild(frame);

const heading = await createHeading(frame, "✌️ Add your title ✌️");
frame.appendChild(heading);

const description = await createDescription(
frame,
heading,
"🪩 Add your description 🪩"
);

this.figma.currentPage.appendChild(frame);
frame.appendChild(heading);
frame.appendChild(description);

const badgeSize = 100;
const currentUserAvatarBadgeStyle: BadgeStyle = {
avatarImage: {
size: badgeSize,
xAxisPosition: this.frameDimensions.width - badgeSize,
yAxisPosition: this.frameDimensions.height - badgeSize,
},
userNameText: {
fontSize: 14,
fontFamily: this.fontFamily,
},
};
await this.currentUserAvatarBadgeCreator.create(
currentUserAvatarBadgeStyle
);

this.figma.currentPage.selection = [heading];

return frame;
Expand Down
@@ -1,18 +1,25 @@
import { NetworkRequestCommand } from "../../browser-commands/network-request/NetworkRequestCommand";
import { CommandHandler } from "../../commands-setup/CommandHandler";
import { executeCommand } from "../../commands-setup/executeCommand";
import { PaintCurrentUserAvatarCommand } from "./PaintCurrentUserAvatarCommand";

export class PaintCurrentUserAvatarCommandHandler
implements CommandHandler<PaintCurrentUserAvatarCommand>
{
private readonly avatarImageSize = 100;

export type BadgeStyle = {
avatarImage: {
size: number;
xAxisPosition: number;
yAxisPosition: number;
};
userNameText: {
fontSize: number;
fontFamily: string;
};
};

export class CurrentUserAvatarBadgeCreator {
constructor(private readonly figma: PluginAPI) {}

// `command` argument needed due to polymorphism.
// eslint-disable-next-line @typescript-eslint/no-unused-vars
handle(command: PaintCurrentUserAvatarCommand): Promise<void> {
create(badgeStyle: BadgeStyle): Promise<void> {
console.log("CurrentUserAvatarBadgeCreator.create(badgeStyle): ");
console.log(badgeStyle);

const currentUserAvatarUrl = this.figma.currentUser?.photoUrl;
const currentUserName = this.figma.currentUser?.name;

Expand All @@ -32,6 +39,7 @@ export class PaintCurrentUserAvatarCommandHandler
this.ensureToOnlyReceiveNetworkRequestResponse(command);

await this.createAvatarBadge(
badgeStyle,
command.payload as ArrayBuffer,
currentUserName as string
);
Expand All @@ -50,28 +58,29 @@ export class PaintCurrentUserAvatarCommandHandler
}

private async createAvatarBadge(
badgeStyle: BadgeStyle,
imageBuffer: ArrayBuffer,
userName: string
): Promise<void> {
const avatarImage = this.createAvatarImage(imageBuffer, userName);
const userNameText = await this.createAvatarText(userName);

const elementsToFocus = [avatarImage, userNameText];
this.figma.currentPage.selection = elementsToFocus;
this.figma.viewport.scrollAndZoomIntoView(elementsToFocus);
this.createAvatarImage(badgeStyle, imageBuffer, userName);
await this.createAvatarText(badgeStyle, userName);
}

private createAvatarImage(
badgeStyle: BadgeStyle,
avatarImage: ArrayBuffer,
currentUserName: string
): EllipseNode {
const imageUint8Array = new Uint8Array(avatarImage);
const figmaImage = this.figma.createImage(imageUint8Array);
const imageWrapper = this.figma.createEllipse();

imageWrapper.x = this.figma.viewport.center.x;
imageWrapper.y = this.figma.viewport.center.y;
imageWrapper.resize(this.avatarImageSize, this.avatarImageSize);
imageWrapper.x = badgeStyle.avatarImage.xAxisPosition;
imageWrapper.y = badgeStyle.avatarImage.yAxisPosition;
imageWrapper.resize(
badgeStyle.avatarImage.size,
badgeStyle.avatarImage.size
);
imageWrapper.fills = [
{ type: "IMAGE", scaleMode: "FILL", imageHash: figmaImage.hash },
];
Expand All @@ -82,17 +91,25 @@ export class PaintCurrentUserAvatarCommandHandler
return imageWrapper;
}

private async createAvatarText(userName: string): Promise<TextNode> {
private async createAvatarText(
badgeStyle: BadgeStyle,
userName: string
): Promise<TextNode> {
const userNameText = this.figma.createText();
userNameText.x = this.figma.viewport.center.x - userName.length / 2;
userNameText.y =
this.figma.viewport.center.y +
this.avatarImageSize +
this.avatarImageSize / 12;
badgeStyle.avatarImage.size +
badgeStyle.avatarImage.size / badgeStyle.userNameText.fontSize;

const fontName: FontName = {
family: badgeStyle.userNameText.fontFamily,
style: "Regular",
};
await this.figma.loadFontAsync(fontName);

await this.figma.loadFontAsync(userNameText.fontName as FontName);
userNameText.characters = userName;
userNameText.fontSize = 14;
userNameText.fontSize = badgeStyle.userNameText.fontSize;

return userNameText;
}
Expand Down

This file was deleted.

24 changes: 23 additions & 1 deletion tests/scene-commands/CreatePagesCommandHandler.test.ts
Expand Up @@ -2,21 +2,33 @@ import { mock } from "jest-mock-extended";

import { CreatePagesCommand } from "../../src/scene-commands/create-pages/CreatePagesCommand";
import { CreatePagesCommandHandler } from "../../src/scene-commands/create-pages/CreatePagesCommandHandler";
import { CurrentUserAvatarBadgeCreator } from "../../src/scene-commands/create-pages/CurrentUserAvatarBadgeCreator";
import { figmaPluginApiMockForCreatePagesCommand } from "../figma-mocks/figma-mocks";

describe("CreatePagesCommandHandler", () => {
it("can be instantiated without throwing errors", () => {
const figmaPluginApiMock = mock<PluginAPI>();

const currentUserAvatarBadgeCreator = new CurrentUserAvatarBadgeCreator(
figmaPluginApiMock
);
const commandHandlerInstantiator = () => {
new CreatePagesCommandHandler(figmaPluginApiMock);
new CreatePagesCommandHandler(
currentUserAvatarBadgeCreator,
figmaPluginApiMock
);
};

expect(commandHandlerInstantiator).not.toThrow(TypeError);
});

it("notifies the end used with a farewell message", async () => {
const currentUserAvatarBadgeCreator = new CurrentUserAvatarBadgeCreator(
figmaPluginApiMockForCreatePagesCommand
);

const commandHandler = new CreatePagesCommandHandler(
currentUserAvatarBadgeCreator,
figmaPluginApiMockForCreatePagesCommand
);
const command = new CreatePagesCommand();
Expand All @@ -27,7 +39,12 @@ describe("CreatePagesCommandHandler", () => {
});

it("rename Cover Page", async () => {
const currentUserAvatarBadgeCreator = new CurrentUserAvatarBadgeCreator(
figmaPluginApiMockForCreatePagesCommand
);

const commandHandler = new CreatePagesCommandHandler(
currentUserAvatarBadgeCreator,
figmaPluginApiMockForCreatePagesCommand
);
const command = new CreatePagesCommand();
Expand All @@ -38,7 +55,12 @@ describe("CreatePagesCommandHandler", () => {
});

it("create secondary pages", async () => {
const currentUserAvatarBadgeCreator = new CurrentUserAvatarBadgeCreator(
figmaPluginApiMockForCreatePagesCommand
);

const commandHandler = new CreatePagesCommandHandler(
currentUserAvatarBadgeCreator,
figmaPluginApiMockForCreatePagesCommand
);
const command = new CreatePagesCommand();
Expand Down